Python迭代器缩多,生成器和裝飾器

Iterators

遵循迭代器協(xié)議呆奕,Python迭代器對象需要支持兩種方法。

iter返回迭代器對象本身瞧壮。這用于forin語句。

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
image

歡迎大家關(guān)注公眾號:「Python精選」,關(guān)注公眾號俄删,回復(fù)「1024」你懂得宏怔,免費領(lǐng)取 6 本經(jīng)典Python編程書籍。關(guān)注我抗蠢,與 10 萬程序員一起進步举哟。每天更新Python干貨哦思劳,期待你的到來迅矛!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市潜叛,隨后出現(xiàn)的幾起案子秽褒,更是在濱河造成了極大的恐慌,老刑警劉巖威兜,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件销斟,死亡現(xiàn)場離奇詭異,居然都是意外死亡椒舵,警方通過查閱死者的電腦和手機蚂踊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笔宿,“玉大人犁钟,你說我怎么就攤上這事∑瞄伲” “怎么了涝动?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炬灭。 經(jīng)常有香客問我醋粟,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任米愿,我火速辦了婚禮厦凤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吗货。我一直安慰自己泳唠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布宙搬。 她就那樣靜靜地躺著笨腥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪勇垛。 梳的紋絲不亂的頭發(fā)上脖母,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音闲孤,去河邊找鬼谆级。 笑死,一個胖子當(dāng)著我的面吹牛讼积,可吹牛的內(nèi)容都是我干的肥照。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼勤众,長吁一口氣:“原來是場噩夢啊……” “哼舆绎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起们颜,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吕朵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后窥突,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體努溃,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年阻问,在試婚紗的時候發(fā)現(xiàn)自己被綠了梧税。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡称近,死狀恐怖第队,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情煌茬,我是刑警寧澤斥铺,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站坛善,受9級特大地震影響晾蜘,放射性物質(zhì)發(fā)生泄漏邻眷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一剔交、第九天 我趴在偏房一處隱蔽的房頂上張望肆饶。 院中可真熱鬧,春花似錦岖常、人聲如沸驯镊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽板惑。三九已至,卻和暖如春偎快,著一層夾襖步出監(jiān)牢的瞬間冯乘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工晒夹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留裆馒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓丐怯,卻偏偏與公主長得像喷好,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子读跷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容