1. 程式人生 > >聰哥哥教你學Python之高階特性

聰哥哥教你學Python之高階特性

記得曾經在學Java的時候,通常是這麼學的,從基礎的語法高階特性,這一塊統稱為JavaSE,然後再到JavaWeb,最後到框架這一塊。那麼Python也有其對應的高階特性。其實在聰哥哥我看來,無論任何程式語言,基本都是這一套。只不過每個程式設計在其特有的領域有其特定的優勢。比如拿區塊鏈來說,其實Java也可以做。只不過沒有Python或者是Go做起來相對那麼輕鬆。

再比如Java可以用於開發客戶端應用、網頁端(web應用)等等,同樣Python也可以。

今天聰哥哥說高階特性,主要圍繞這麼幾個方面?

(1)切片;

(2)迭代;

(3)列表生成式;

(4)生成器;

(5)迭代器;

一、切片

切片這個玩意不好用語言來表達,不過聰哥哥我還是得說一下。

經常取指定範圍的索引叫做切片,一聽切片切片,肯定是切一個整體的某一部分。

沒有切片之前,我們是怎麼取資料的

示例一(取前三個資料):

# -*- coding: utf-8 -*-
L = ['A', 'B', 'C', 'D', 'E']
print([L[0], L[1], L[2]])

通過L[0]這種方式去取,感覺很麻煩是不是,或許有人說,寫個for迴圈直接遍歷就行。那麼如果是比遍歷的話,就需要加對應的判斷程式碼,因為我需要的是某一段資料值,遍歷還是太麻煩了。

不過別怕,Python高階特性之一的切片即能做到非常的簡化,又能達到對應的目的。

示例二:

# -*- coding: utf-8 -*-
L = ['A', 'B', 'C', 'D', 'E']
print(L[:3])

示例一和示例二的效果是一樣的,但是示例二比示例一更簡化。

L[:3] 所代表的就是初索引為0,往前取三個值。如果初索引不為0,你就需要指定,例如L[1:3]這樣的。

 

二、迭代

什麼是迭代?

引用百度百科:

迭代是重複反饋過程的活動,其目的通常是為了逼近所需目標或結果。每一次對過程的重複稱為一次“迭代”,而每一次迭代得到的結果會作為下一次迭代的初始值。

重複執行一系列運算步驟,從前面的量依次求出後面的量的過程。此過程的每一次結果,都是由對前一次所得結果施行相同的運算步驟得到的。例如利用迭代法*求某一數學問題的解。

對計算機特定程式中需要反覆執行的子程式*(一組指令),進行一次重複,即重複執行程式中的迴圈,直到滿足某條件為止,亦稱為迭代。

在這所表示的是:如果給定一個list或tuple,我們可以通過for迴圈來遍歷這個list或tuple,這種遍歷我們稱為迭代(Iteration)。

示例一:

# -*- coding: utf-8 -*-
d = {'a': 1, 'b': 2, 'c': 3}
for key in d:
	print(key)

 

這裡或許會有人疑問,如何判斷這個物件是可迭代的?

通過collections模組的Iterable型別判斷

示例二(如何判斷這個物件是可迭代的):

# -*- coding: utf-8 -*-
from collections import Iterable
print(isinstance('abc', Iterable))

當最終輸出的結果為True,就表示這個物件是可迭代的。

任何可迭代物件都可以作用於for迴圈,包括我們自定義的資料型別,只要符合迭代條件,就可以使用for迴圈

 

三、列表生成式

列表生成式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的生成式。

舉例說明:要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))

 

原來我們是怎麼做的?

示例一:

# -*- coding: utf-8 -*-
print(list(range(1, 11)))

資料範圍比較小還行,如果換成這樣:

要生成[1x1, 2x2, 3x3, ..., 10x10]這樣的,怎麼做?

肯定是要用到迴圈的。

示例二:

# -*- coding: utf-8 -*-
L = []
for x in range(1, 11):
   L.append(x * x)
print(L)

但是迴圈太繁瑣,而列表生成式則可以用一行語句代替迴圈生成上面的list:

示例三:

# -*- coding: utf-8 -*-
print([x * x for x in range(1, 11)])

示例三同示例二的效果是一樣的,但是從簡潔性來看似乎示例三稍微比for迴圈要好點。或許有人說:我覺得還是for迴圈好,這裡了也看不出比for迴圈好在那。聰哥哥我也不能說的太細,說的太細,過於高深,反而講偏。一句話:仁者見仁,智者見智。

接下來可能有人會罵我說。好吧,我只能硬著頭皮說說呢,就拿L.append說事,每次追加資料,都相當於新增一個地址和引用會有一定的記憶體開銷。少的資料還好,如果是大資料呢。

 

再比如使用列表生成式可以使程式碼更簡潔,例如我要獲取當前目錄下的所有檔案:

示例四:

# -*- coding: utf-8 -*-
import os
print([d for d in os.listdir('.')])

聰哥哥有話說:運用列表生成式,可以快速生成list,可以通過一個list推匯出另一個list,而程式碼卻十分簡潔。

 

四、生成器

通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator。

示例(簡單示例):

# -*- coding: utf-8 -*-
g = (x * x for x in range(10))
for n in g:
    print(n)

聰哥哥有話說:

generator是非常強大的工具,在Python中,可以簡單地把列表生成式改成generator,也可以通過函式實現複雜邏輯的generator。要理解generator的工作原理,它是在for迴圈的過程中不斷計算出下一個元素,並在適當的條件結束for迴圈。對於函式改成的generator來說,遇到return語句或者執行到函式體最後一行語句,就是結束generator的指令,for迴圈隨之結束。

 

五、迭代器

我們已經知道,可以直接作用於for迴圈的資料型別有以下幾種:

一類是集合資料型別,如listtupledictsetstr等;

一類是generator,包括生成器和帶yield的generator function。

這些可以直接作用於for迴圈的物件統稱為可迭代物件:Iterable

示例一:

# -*- coding: utf-8 -*-
from collections import Iterator
print(isinstance((x for x in range(10)), Iterator))

生成器都是Iterator物件,但listdictstr雖然是Iterable,卻不是Iterator。

 

示例二(把listdictstrIterable變成Iterator可以使用iter()函式):

# -*- coding: utf-8 -*-
from collections import Iterator
print(isinstance(iter([]), Iterator))

你可能會問,為什麼listdictstr等資料型別不是Iterator

這是因為Python的Iterator物件表示的是一個數據流,Iterator物件可以被next()函式呼叫並不斷返回下一個資料,直到沒有資料時丟擲StopIteration錯誤。可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函式實現按需計算下一個資料,所以Iterator的計算是惰性的,只有在需要返回下一個資料時它才會計算。

Iterator甚至可以表示一個無限大的資料流,例如全體自然數。而使用list是永遠不可能儲存全體自然數的。

最後聰哥哥有話說:

凡是可作用於for迴圈的物件都是Iterable型別;

凡是可作用於next()函式的物件都是Iterator型別,它們表示一個惰性計算的序列;

集合資料型別如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函式獲得一個Iterator物件。