Iterators
遵循迭代器協(xié)議呆奕,Python迭代器對象需要支持兩種方法。
iter返回迭代器對象本身瞧壮。這用于for 和in語句。
next方法返回迭代器中的下一個值匙握。如果沒有更多的項目要返回咆槽,那么它應(yīng)該引發(fā)StopIteration異常。
class Counter(object):
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
'Returns itself as an iterator object'
return self
def __next__(self):
'Returns the next value till current is lower than high'
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
現(xiàn)在我們可以在代碼中使用這個迭代器圈纺。
>>> c = Counter(5,10)
>>> for i in c:
... print(i, end=' ')
...
5 6 7 8 9 10
請記住秦忿,迭代器對象只能使用一次。這意味著在它提升 一次StopIteration之后蛾娶,它將繼續(xù)引發(fā)相同的異常灯谣。
>>> c = Counter(5,6)
>>> next(c)
5
>>> next(c)
6
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in next
StopIteration
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in next
StopIteration
在我們看到的for循環(huán)示例中使用迭代器,下面的示例嘗試顯示幕后代碼蛔琅。
>>> iterator = iter(c)
>>> while True:
... try:
... x = iterator.__next__()
... print(x, end=' ')
... except StopIteration as e:
... break
...
5 6 7 8 9 10
Generators
在本節(jié)中胎许,我們將了解Python生成器。它們是在Python 2.3中引入的罗售。使用函數(shù)中的關(guān)鍵字yield創(chuàng)建迭代器是一種更簡單的方法辜窑。
>>> def my_generator():
... print("Inside my generator")
... yield 'a'
... yield 'b'
... yield 'c'
...
>>> my_generator()
<generator object my_generator at 0x7fbcfa0a6aa0>
在上面的例子中,我們使用yield語句創(chuàng)建一個簡單的生成器寨躁。我們可以在for循環(huán)中使用它穆碎,就像我們使用任何其他迭代器一樣。
>>> for char in my_generator():
... print(char)
...
Inside my generator
a
b
c
在下一個示例中职恳,我們將使用生成器函數(shù)創(chuàng)建相同的Counter類所禀,并在for循環(huán)中使用它。
def counter_generator(low, high):
while low <= high:
yield low
low += 1
>>> for i in counter_generator(5,10):
... print(i, end=' ')
...
5 6 7 8 9 10
當(dāng)while循環(huán)到達yield語句時放钦,返回low值并暫停生成器狀態(tài)色徘。在第二次下一次調(diào)用期間,生成器恢復(fù)到之前凍結(jié)的位置操禀,然后將low的值增加1贺氓。它繼續(xù)while循環(huán)并再次進入yield語句。
當(dāng)您調(diào)用生成器函數(shù)時,它返回* generator 對象辙培。如果你在這個對象上調(diào)用 dir 蔑水,你會發(fā)現(xiàn)它在其他方法中包含iter和 __ next __ *方法。
>>> dir(c)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__',
'__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'close', 'gi_code', 'gi_frame', 'gi_running', 'send', 'throw']
我們主要使用生成器進行l(wèi)aze評估扬蕊。這樣搀别,生成器成為處理大量數(shù)據(jù)的好方法。如果您不想加載內(nèi)存中的所有數(shù)據(jù)尾抑,可以使用一次生成器歇父,它會一次傳遞每個數(shù)據(jù)。
這種示例的最大例子之一是os.path.walk()函數(shù)再愈,它使用回調(diào)函數(shù)和當(dāng)前的os.walk生成器榜苫。使用生成器實現(xiàn)可以節(jié)省內(nèi)存。
我們可以有生成無限值的生成器翎冲。以下是一個這樣的例子碍讨。
>>> def infinite_generator(start=0):
... while True:
... yield start
... start += 1
...
>>> for num in infinite_generator(4):
... print(num, end=' ')
... if num > 20:
... break
...
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
如果我們回到my_generator的例子拆火,我們會發(fā)現(xiàn)生成器的一個特性川慌。它們不可重復(fù)使用脸狸。
>>> g = my_generator()
>>> for c in g:
... print(c)
...
Inside my generator
a
b
c
>>> for c in g:
... print(c)
...
創(chuàng)建可重用生成器的一種方法是基于對象的生成器,它不保持任何狀態(tài)缴渊。任何產(chǎn)生數(shù)據(jù)的iter方法的類都可以用作對象生成器赏壹。在下面的例子中,我們將重新創(chuàng)建計數(shù)器生成器衔沼。
>>> class Counter(object):
... def __init__(self, low, high):
... self.low = low
... self.high = high
... def __iter__(self):
... counter = self.low
... while self.high >= counter:
... yield counter
... counter += 1
...
>>> gobj = Counter(5, 10)
>>> for num in gobj:
... print(num, end=' ')
...
5 6 7 8 9 10
>>> for num in gobj:
... print(num, end=' ')
...
5 6 7 8 9 10
生成器表達式
在本節(jié)中蝌借,我們將學(xué)習(xí)生成器表達式,它是列表推導(dǎo)和生成器的高性能指蚁,內(nèi)存有效概括骨望。
例如,我們將嘗試將所有數(shù)字的平方從1加到9欣舵。
>>> sum([x*x for x in range(1,10)])
該示例實際上首先在內(nèi)存中創(chuàng)建一個方形值列表擎鸠,然后迭代它,最后在求和后釋放內(nèi)存缘圈。如果列出大的列表劣光,您可以了解內(nèi)存使用情況。
我們可以使用生成器表達式來節(jié)省內(nèi)存使用量糟把。
sum(x*x for x in range(1,10))
生成器表達式的語法表示總是需要直接在一組括號內(nèi)绢涡,并且兩邊都不能有逗號。這基本上意味著以下兩個示例都是有效的生成器表達式用法示例
>>> sum(x*x for x in range(1,10))
285
>>> g = (x*x for x in range(1,10))
>>> g
<generator object <genexpr> at 0x7fc559516b90>
我們可以鏈接生成器或生成器表達式遣疯。在下面的示例中雄可,我們將讀取文件* / var / log / cron *并查找是否有任何特定作業(yè)(在我們搜索anacron的示例中)是否成功運行。
我們可以使用shell命令tail -f / var / log / cron | grep anacron來做同樣的事情
>>> jobtext = 'anacron'
>>> all_lines = (line for line in open('/var/log/cron', 'r') )
>>> job = ( line for line in all_lines if line.find(jobtext) != -1)
>>> text = next(job)
>>> text
"May 6 12:17:15 dhcp193-104 anacron[23052]: Job `cron.daily' terminated\n"
>>> text = next(job)
>>> text
'May 6 12:17:15 dhcp193-104 anacron[23052]: Normal exit (1 job run)\n'
>>> text = next(job)
>>> text
'May 6 13:01:01 dhcp193-104 run-parts(/etc/cron.hourly)[25907]: starting 0anacron\n'
你可以在行上寫一個for循環(huán)。
閉包
閉包只不過是另一個函數(shù)返回的函數(shù)数苫。我們使用閉包來刪除代碼重復(fù)聪舒。在以下示例中,我們創(chuàng)建了一個用于添加數(shù)字的簡單閉包虐急。
>>> def add_number(num):
... def adder(number):
... 'adder is a closure'
... return num + number
... return adder
...
>>> a_10 = add_number(10)
>>> a_10(21)
31
>>> a_10(34)
44
>>> a_5 = add_number(5)
>>> a_5(3)
8
adder是一個閉包箱残,它將給定的數(shù)字添加到預(yù)定義的數(shù)字中。
裝飾者
Decorator是為某些對象動態(tài)添加一些新行為的方法止吁。我們通過使用閉包在Python中實現(xiàn)了相同的功能被辑。
在這個例子中,我們將創(chuàng)建一個簡單的例子敬惦,它將在執(zhí)行函數(shù)之前和之后打印一些語句盼理。
>>> def my_decorator(func):
... def wrapper(*args, **kwargs):
... print("Before call")
... result = func(*args, **kwargs)
... print("After call")
... return result
... return wrapper
...
>>> @my_decorator
... def add(a, b):
... "Our add function"
... return a + b
...
>>> add(1, 3)
Before call
After call
4
歡迎大家關(guān)注公眾號:「Python精選」,關(guān)注公眾號俄删,回復(fù)「1024」你懂得宏怔,免費領(lǐng)取 6 本經(jīng)典Python編程書籍。關(guān)注我抗蠢,與 10 萬程序員一起進步举哟。每天更新Python干貨哦思劳,期待你的到來迅矛!