1.1???生成器

通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。

所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator

1.1.1???第一種()生成器

>>> L = [ x * x for x inrange(2,8)]??? --列表生成表達式

>>> L

[4, 9, 16, 25, 36, 49]

>>> G = (x * x for x in range(2,8))??? --[]符號變成了(),即生成器

>>> G

<generator object <genexpr> at0x2b25180303b8>

>>> next(G)

4

>>> next(G)?????? --依次計算下個元素

9

>>> next(G)

16

別忘generator也是個可迭代對象

>>> G = ( x * x for x inrange(2,8))

>>> for i in G:

...????print(i)

...

4

9

16

25

36

49

1.1.2???第二種yield生成器

這里yield理解成生產,產生的意思。

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

>>> def step():

...????print('step 1')

...????yield 1

...????print('step 2')

...????yield 2

...????print('step 3')

...????yield 3

...

>>>

>>>

>>> next(step())

step 1

1

>>> next(step())

step 1

1

>>> o = step()

>>> next(o)

step 1

1

>>> next(o)

step 2

2

>>> next(o)

step 3

3

>>> next(o)????? --迭代溢出

Traceback (most recent call last):

?File "<stdin>", line 1, in <module>

StopIteration

定義一個yield生成器

>>> def fib(max):

...????n, a, b = 0, 0, 1

...????while n < max:

...????????yield b

...????????a, b = b, a + b

...????????n = n + 1

...????return 'done'

...

>>>

>>>

for迭代生成器中的元素

>>> for i in fib(6):

...????print(i)

...

1

1

2

3

5

8

>>>

生成器中return返回值

>>> g = fib(6)

>>> while True:

...????try:

...????????x = next(g)

...????????print('x =',x)

...????except StopIteration as e:

...????????print('Generator return value: ',e.value)

...????????break

...

x = 1

x = 1

x = 2

x = 3

x = 5

x = 8

Generator return value:? done

1.2???迭代器

可以直接作用于for循環的數據類型有以下幾種:

一類是集合數據類型,如listtupledictsetstr等;

一類是generator,包括生成器和帶yieldgenerator function

這些可以直接作用于for循環的對象統稱為可迭代對象:Iterable

可以使用isinstance()判斷一個對象是否是Iterable對象:

>>> fromcollections import Iterable

>>> isinstance([], Iterable)

True

>>> isinstance({}, Iterable)

True

>>> isinstance('abc', Iterable)

True

>>> isinstance(((1, 2), (3, 4)),Iterable)

True

>>> isinstance((x for x inrange(10)), Iterable)

True

>>> isinstance(100, Iterable)

False

生成器不但可以作用于for循環,還可以被next()函數不斷調用并返回下一個值,直到最后拋出StopIteration錯誤表示無法繼續返回下一個值了。

可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator

可以使用isinstance()判斷一個對象是否是Iterator對象:

>>> fromcollections import Iterator

>>> isinstance([], Iterator)

False

>>> isinstance({}, Iterator)

False

>>> isinstance(((1, 2), (3, 4)),Iterator)

False

>>> isinstance((x for x inrange(10)), Iterator)

True

?

生成器都是Iterator對象,但listdictstr雖然是Iterable,卻不是Iterator

listdictstrIterable變成Iterator可以使用iter()函數

>>> isinstance(iter([]), Iterator)

True

>>> isinstance(iter('abc'),Iterator)

True

你可能會問,為什么listdictstr等數據類型不是Iterator

這是因為PythonIterator對象表示的是一個數據流,Iterator對象可以被next()函數調用并不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。

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

?

凡是可作用于for循環的對象都是Iterable類型;

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

集合數據類型如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。

Pythonfor循環本質上就是通過不斷調用next()函數實現的,例如:

for x in [1, 2, 3, 4, 5]:

???pass

實際上完全等價于:

# 首先獲得Iterator對象:

it = iter([1, 2, 3, 4,5])

# 循環:

while True:

???try:

???????# 獲得下一個值:

???????x = next(it)

???except StopIteration:

???????# 遇到StopIteration就退出循環

???????break