用 python 差不多半年多了,從去年暑假開始接觸,從開始的懵逼,到寫了一些小爬蟲總算入門之後,許多作業也是能用 python 就用 python,基本拋棄了 C++。但還是有些過於急躁了,能夠寫一些簡短的程式碼,但是對於 python 的很多特性都不知道或者忘記了,這裡回去廖大教程複習一下,順便記錄下我覺得比較重要的地方。
本文主要記錄廖大教程中高級特性這一節的內容,並寫下我的一些理解。在我看來,這些特性是很pythonic 的,用在程式碼中很有bigger 啊~
切片和迭代就不說了,這裡直接先看一下列表生成式吧,從名字就能大概猜出這是產生清單的一些方法,例如:如何產生[1*1, 2*2, ... ,10*10]
?可以用循環不斷在列表尾部添加元素,如果使用pythonic 的方法,也就是列表生成式,則是:
>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
後面還能跟上if 判斷,例如:
>>> [x * x for x in range(1, 11) if x%2==0] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
這樣,本來需要使用循環寫4, 5行的程式碼,使用一行就解決了,直覺明了。
還能使用兩個 for 迴圈產生全排列:
>>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
這樣如何加入 if 判斷呢?可以在每個for 語句後面加,或者在最後加上:
>>> [m + n for m in 'ABC' if m < 'C' for n in 'XYZ' if n < 'Z'] ['AX', 'AY', 'BX', 'BY'] >>> [m + n for m in 'ABC' for n in 'XYZ' if n < 'Z' and m < 'C'] ['AX', 'AY', 'BX', 'BY']
也可以同時在一個for 語句中迭代多個變量,例如dict的items()可以同時迭代key和value:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> [k + '=' + v for k, v in d.items()] ['y=B', 'x=A', 'z=C']
差不多就是這樣了~
但是以前總是寫C++ ,這種思維模式很難改過來,只能慢慢在使用中熟悉這種語法,習慣了就能夠在下意識中寫出來了。
為什麼要使用生成器?廖大的教學中說得很詳細,這裡再簡述一下:
因為列表的內容放在記憶體中,而受到記憶體限制,列表的容量有限。
如果我們只訪問極少的元素,那麼存在極大的空間浪費。
而生成器可以一邊迭代一邊計算下一個值,理論上,該過程可以無限進行下去,並且不會佔用大量內存。
這裡只是簡單介紹一下,更詳細的請 Google 哈~
如何創建生成器?第一種方法類似前面講到的列表產生式,只需要將[]改為()即可:
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>
可以看到,方法上大致相同,[]得到的是一個已經得到所有值的列表,()得到的是一個生成器,它們都能使用for 循環來迭代,但是生成器不能使用下標訪問,並且只能被迭代一次,再次迭代則會有StopIteration 的異常:
>>> for i in g: ... print(i) ... 0 1 4 9 16 25 36 49 64 81 >>> for i in g: ... print(i) ... >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
不過當我們創建了一個generator後,基本上永遠不會呼叫next(),而是透過for循環來迭代它,並且不需要關心StopIteration的錯誤。
如果推算的演算法比較複雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現,比如,著名的斐波那契數列:
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done'
關於yield 這個關鍵字,我在剛學python 的時候也糾結了很久,直到看到生成器的時候才大致明白,大家搜尋一下就能大致明白了,我覺得這東西說起來麻煩,只說一兩句又怕說錯。廖大的教學中是這樣說的:
函數是順序執行,遇到return語句或最後一行函數語句就回傳。而變成generator的函數,在每次呼叫next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
可能有點難理解,不過明白了就很好說了。
當然,函數中還可以加入 return,在一個 generator function 中,如果沒有 return,則預設執行至函數完畢,如果在執行過程中 return,則直接拋出 StopIteration 終止迭代。
例如上面的例子,我們在迭代時發現並沒有出現'done' 這串字符,是因為return 的值被當作Exception Value 了,如果要顯示出來,則可以這樣:
>>> g = fib(6) >>> while True: ... try: ... x = next(g) ... print('g:', x) ... except StopIteration as e: ... print('Generator return value:', e.value) ... break ... g: 1 g: 1 g: 2 g: 3 g: 5 g: 8 Generator return value: done
可直接作用於for 迴圈的物件稱為可迭代對象,可以用isinstance() 函數判斷是否為可迭代對象:
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False
而可以被next()函數呼叫並不斷傳回下一個值的對象稱為迭代器:Iterator。當然,仍然可以使用isinstance()判斷一個物件是否是Iterator物件:
>>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False
透過上面兩個例子,可以這樣理解:生成器和list,tuple,str 等都是Iterable 對象,生成器同時還是Iterator 對象,而list 等不是。那麼能否直接將 Iterable 物件轉換成 Iterator 物件呢?
可以使用iter()函數:
>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True
其实,Iterator 对象表示的是一个数据流,我们可以把这个数据流看做是一个有序序列,但却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以 Iterator 的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,但 list,tuple 什么的是不可能这样的。
更多关于 python 的一些高级特性相关文章请关注PHP中文网!