廖雪峯Python3 學習筆記--切片、迭代、列表生成式、生成器、迭代器

1、切片css

一、切片支持list,tuple和string;python

二、切片的基本格式 object[start:end:step],切片結果不包含object[end];算法

三、倒數切片ruby

>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob']

2、迭代

一、判斷一個對象是可迭代對象呢?方法是經過collections模塊的Iterable類型判斷:app

 
>>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True >>> isinstance(123, Iterable) # 整數是否可迭代 False

二、默認狀況下,dict迭代的是key。若是要迭代value,能夠用for value in d.values(),若是要同時迭代key和value,能夠用for k, v in d.items()函數

因爲字符串也是可迭代對象,所以,也能夠做用於for循環;ui

三、Python內置的enumerate函數能夠把一個list變成索引-元素對,這樣就能夠在for循環中同時迭代索引和元素自己:spa

>>> for i, value in enumerate(['A', 'B', 'C']): ... print(i, value) ... 0 A 1 B 2 C 

上面的for循環裏,同時引用了兩個變量,在Python裏是很常見的,好比下面的代碼:code

>>> for x, y in [(1, 1), (2, 4), (3, 9)]: ... print(x, y) ... 1 1 2 4 3 9

3、列表生成式(List Comprehensions)對象

若是要生成[1x1, 2x2, 3x3, ..., 10x10]怎麼作?方法一是循環:

>>> L = [] >>> for x in range(1, 11): ... L.append(x * x) ... >>> L [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 

可是循環太繁瑣,而列表生成式則能夠用一行語句代替循環生成上面的list:

>>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 

寫列表生成式時,把要生成的元素x * x放到前面,後面跟for循環,就能夠把list建立出來,十分有用,多寫幾回,很快就能夠熟悉這種語法。

for循環後面還能夠加上if判斷,這樣咱們就能夠篩選出僅偶數的平方:

>>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100] 

還可使用兩層循環,能夠生成全排列

>>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] 

三層和三層以上的循環就不多用到了。

4、生成器

經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator。

要建立一個generator,有不少種方法。第一種方法很簡單,只要把一個列表生成式的[]改爲(),就建立了一個generator

>>> 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> 

建立Lg的區別僅在於最外層的[]()L是一個list,而g是一個generator。

咱們能夠經過next()函數得到generator的下一個返回值:

>>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> next(g) 16 >>> next(g) 25 >>> next(g) 36 >>> next(g) 49 >>> next(g) 64 >>> next(g) 81 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

generator保存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤。

固然,上面這種不斷調用next(g)實在是太變態了,正確的方法是使用for循環,由於generator也是可迭代對象:

>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n)

建立了一個generator後,基本上永遠不會調用next(),而是經過for循環來迭代它,而且不須要關心StopIteration的錯誤。
next()forStopIteration
定義generator的另外一種方法,若是一個函數定義中包含關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generator:
yield

generator和函數的執行流程不同。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。

舉個簡單的例子,定義一個generator,依次返回數字1,3,5:

def odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5) 

調用該generator時,首先要生成一個generator對象,而後用next()函數不斷得到下一個返回值:

>>> o = odd()
>>> next(o) step 1 1 >>> next(o) step 2 3 >>> next(o) step 3 5 >>> next(o) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

能夠看到,odd不是普通函數,而是generator,在執行過程當中,遇到yield就中斷,下次又繼續執行。執行3次yield後,已經沒有yield能夠執行了,因此,第4次調用next(o)就報錯。

回到fib的例子,咱們在循環過程當中不斷調用yield,就會不斷中斷。固然要給循環設置一個條件來退出循環,否則就會產生一個無限數列出來。

一樣的,把函數改爲generator後,咱們基本上歷來不會用next()來獲取下一個返回值,而是直接使用for循環來迭代:

>>> for n in fib(6): ... print(n) ... 1 1 2 3 5 8 

可是用for循環調用generator時,發現拿不到generator的return語句的返回值。若是想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIterationvalue中:



>>> 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循環的對象都是Iterable類型;

凡是可做用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;

集合數據類型如listdictstr等是Iterable但不是Iterator,不過能夠經過iter()函數得到一個Iterator對象。

Python的for循環本質上就是經過不斷調用next()函數實現的

相關文章
相關標籤/搜索