聰哥哥教你學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
迴圈的資料型別有以下幾種:
一類是集合資料型別,如list
、tuple
、dict
、set
、str
等;
一類是generator
,包括生成器和帶yield
的generator function。
這些可以直接作用於for
迴圈的物件統稱為可迭代物件:Iterable
。
示例一:
# -*- coding: utf-8 -*-
from collections import Iterator
print(isinstance((x for x in range(10)), Iterator))
生成器都是Iterator
物件,但list
、dict
、str
雖然是Iterable
,卻不是Iterator。
示例二(把list
、dict
、str
等Iterable
變成Iterator
可以使用iter()
函式):
# -*- coding: utf-8 -*-
from collections import Iterator
print(isinstance(iter([]), Iterator))
你可能會問,為什麼list
、dict
、str
等資料型別不是Iterator
?
這是因為Python的Iterator
物件表示的是一個數據流,Iterator物件可以被next()
函式呼叫並不斷返回下一個資料,直到沒有資料時丟擲StopIteration
錯誤。可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()
函式實現按需計算下一個資料,所以Iterator
的計算是惰性的,只有在需要返回下一個資料時它才會計算。
Iterator
甚至可以表示一個無限大的資料流,例如全體自然數。而使用list是永遠不可能儲存全體自然數的。
最後聰哥哥有話說:
凡是可作用於for
迴圈的物件都是Iterable
型別;
凡是可作用於next()
函式的物件都是Iterator
型別,它們表示一個惰性計算的序列;
集合資料型別如list
、dict
、str
等是Iterable
但不是Iterator
,不過可以通過iter()
函式獲得一個Iterator
物件。