《Python高階程式設計》(三)生成器
阿新 • • 發佈:2019-01-10
生成器
定義
生成器是一個函式,它並不執行並返回一個單一值,而是按照順序返回一個或多個值
生成器的語法
生成器函式的特徵就是在函式內部有一個或多個yield語句。
Python 2中,yield和return不能共存;python 3中可同時存在。
yield語句:命令函式返回一個值給呼叫者,但不會終止函式的執行。執行會暫時停頓直到呼叫程式碼重新恢復生成器,在停止的地方再次開始執行。
生成器案例
import time
def fib():
numbers = []
while True:
if len(numbers) < 2:
numbers.append(1)
else:
numbers.append(sum(numbers))
numbers.pop(0)
yield numbers[-1]
f = fib()
for i in f:
print i
time.sleep(1)
- 注意:f = fib()語句執行後,函式程式碼並沒有執行,直譯器唯一完成的就是識別生成器的出現並返回一個generator物件,該物件在每執行一次程式碼時就請求一個值。
- 可以使用內建的next函式請求第一個值
- 函式只是儲存了最新的兩個數字,沒有把龐大的數列儲存在記憶體
- TopIterator異常:迭代生成器丟擲StopIteration時,標誌著生成器迭代完成並已退出
- python3中消除了yield和return不能在一個函式中共存的限制。但return等同於raise StopIteration
- yield語句實際上是一個表示式,有返回值
生成器之間的互動
一個求平凡的生成器
def squares(): cursor = 1 while True: yield cursor ** 2 cursor += 1 sq = squares() print sq.next() # 1 print sq.next() # 4
該生成器是單向的,send方法允許生成器反向溝通
def squares1(cursor=1):
while True:
response = yield cursor ** 2
if response:
cursor = int(response)
else:
cursor += 1
sq = squares1()
print sq.next() # 1
print sq.next() # 4
print sq.send(7) # 49;值7傳送給生成器,賦給cursor變數
print sq.next() # 64
- send方法:允許生成器反向溝通,因為yield語句實際上就是一個表示式
- send的目的是提供一個與生成器雙向互動的機制,確定是否(如何)處理髮送給生成器的值是生成器的責任
迭代物件和迭代器
- 迭代器 是指包含__next__方法的任何物件。生成器是一種迭代器。range是迭代器,但不是生成器
- 迭代物件 是指任何定義了__iter__方法的物件。可迭代物件的__iter__方法負責返回一個迭代器
print next(range(5)) #是迭代器,但不是生成器
TypeError: list object is not an iterator
print range(5).__iter__()
<listiterator object at 0x01662BB0>
標準庫中的生成器
- range(前一節)
- dict.items及其家族
- python 2中:iterkeys,itervalues,iteritems
- python 3中:keys,values,items
- 如果在迭代期間試圖修改字典,則會報RuntimeError。因為items迭代器是一個僅從引用的字典中讀取資料的生成器,如果執行時字典表發生了變化,它將不知道自己應該做什麼,因此丟擲異常。
- 案例
dictionary = {'foo':'bar', 'baz':'bacon'}
iterator = iter(dictionary.items())
print next(iterator)
print next(iterator)
執行結果:
(‘foo’, ‘bar’)
(‘baz’, ‘bacon’)
- zip:使用zip的目的是在不同的結構中輸出其迭代物件的返回成員,一次輸出一個集合。緩解了對記憶體的需求。
>>> z = zip(['a','b','c','d'], ['x','y','z'])
>>> next(z)
('a', 'x')
>>> next(z)
('b', 'y')
>>> next(z)
('c', 'z')
>>> next(z)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
next(z)
StopIteration
- map:該函式將一個能接受N個引數和N個迭代物件的函式作為引數,並且計算每個迭代物件的序列成員的函式結果
函式定義:map()是 Python 內建的高階函式,它接收一個函式 f 和一個 list,並通過把函式 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回。
>>> m = map(lambda x,y: max([x,y]), [4,1,7], [3,4,5])
>>> m
<map object at 0x0208CB50>
>>> next(m)
4
>>> next(m)
4
>>> next(m)
7
>>> next(m)
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
next(m)
StopIteration
- 檔案物件:readline方法可以一次讀取一行
>>> f = open('aa.txt') #open函式返回的結果物件除了其他身份外,還是個生成器
>>> next(f)
'one line\n'
>>> next(f)
'two line\n'
>>> next(f)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
next(f)
StopIteration
何時編寫生成器
- 原則:只有當需要值時才會確定這個值,而不是提前準備好;即使需要所有資料,但是如果不需要一次性處理所有資料,仍然可以僅儲存需要的資料。
- 兩個理由:
- 分塊訪問資料:需要涵蓋必須分塊訪問資料的情況,但是這種情況沒必要儲存整個副本(例如readline和dict.items)
- 分塊計算資料:僅在需要它時計算資料(例如range和fibonacci函式)
生成器單例模式
很多生成器是單例模式;簡單的生成器函式不是單例模式
class Fabonacci(object):
def __init__(self):
self.numbers = []
def __iter__(self):
return self
def __next__(self):
if len(self.numbers) < 2:
self.numbers.append(1)
else:
self.numbers.append(sum(self.numbers))
self.numbers.pop(0)
return self.numbers[-1]
def send(self, value):
pass
# For python2 compatibility
next = __next__
f = Fabonacci()
i1 = iter(f)
print next(i1)
print next(i1)
i2 = iter(f)
print next(i2)
執行結果:
1
1
2
- 這是一個Fabonacci類,實現了生成器的協議,它也是一個迭代物件,並且將自己作為引數響應iter,即每個Fabonacci物件只有一個迭代器:它自己
- 注意:弄明白一個迭代物件是否允許有多個迭代器:有些迭代物件可以有多個迭代器,有些迭代物件則不可以
生成器內部的生成器
- 生成器委託:yield from 為生成器提供一種呼叫其他生成器的直接方式。
- 案例:python 3.3之前,將子生成器組合成一個生成器的方法是顯式地迭代它們,即:full_gen
>>> def gen1():
yield 'foo'
yield 'bar'
>>> def gen2():
yield 'spam'
yield 'eggs'
>>> def full_gen():
for word in gen1():
yield word
for word in gen2():
yield word
>>> def fuu_gen2(): #僅python3.3以上版本支援
yield from gen1() #生成器委託
yield from gen2()
>>> f =full_gen()
>>> for i in f:
print i
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(i)?
>>> for i in f:
print (i)
foo
bar
spam
eggs
>>> f = fuu_gen2()
>>> for i in f:
print (i)
foo
bar
spam
eggs