Python中的生成器与yield
對于python中的yield有些疑惑,然后在StackOverflow上看到了一篇回答,所以搬運過來了,英文好的直接看原文吧。
可迭代對象
當你創(chuàng)建一個列表的時候,你可以一個接一個地讀取其中的項。一個接一個地讀項就叫做迭代:
>>> mylist = [1, 2, 3] >>> for i in mylist: ... print(i) 1 2 3mylist就是一個可迭代對象。你使用列表推導式時,就創(chuàng)建了一個列表,也就是一個可迭代對象:
>>> mylist = [x*x for x in range(3)] >>> for i in mylist: ... print(i) 0 1 4任何可以用“for...in...”操作的事物都是可迭代對象;列表,字符串,文件……
這些可迭代對象很方便,因為你可以隨意地訪問它們,但是你把所有的值都保存在內存中。如果你有大量的數(shù)據(jù)的話,這就不是你想要的了。
生成器
生成器是迭代器,但是你只可以對它們進行一次迭代。這是因為你沒有在內存中存儲所有數(shù)值,它們動態(tài)地生成值:
>>> mygenerator = (x*x for x in range(3)) >>> for i in mygenerator: ... print(i) 0 1 4跟上面的例子基本一樣,除了用()代替了[]。但是,你不能第二次執(zhí)行for i in mygenerator,因為生成器只能使用一次;它計算出0,然后忘掉0并計算1,最后是計算4,一個接一個地進行。
Yield
Yield是一個像return一樣的關鍵字,只不過函數(shù)返回的是一個生成器:
>>> def createGenerator(): ... mylist = range(3) ... for i in mylist: ... yield i*i ... >>> mygenerator = createGenerator() # 創(chuàng)建一個迭代器 >>> print(mygenerator) # mygenerator是一個對象! <generator object createGenerator at 0xb7555c34> >>> for i in mygenerator: ... print(i) 0 1 4 >>> for i in mygenerator: print(i)>>>這個例子里的生成器沒什么用,但是如果你知道你的函數(shù)會返回大量的數(shù)據(jù),而你只需要對這些數(shù)據(jù)讀取一次時,那就變得很有用了。
要掌握yield,你必須理解當你訪問函數(shù)的時候,你寫在函數(shù)體中的代碼并沒有執(zhí)行。這個函數(shù)只是返回了迭代器對象,這一點很微妙。
然后,每次for語句使用生成器的時候,你的代碼就會運行。
然后是最難的部分:
for語句第一次調用從你的函數(shù)中創(chuàng)建的迭代器對象時,它就會運行你函數(shù)中的代碼,從開始一直到它碰見yield,然后它就返回這個循環(huán)中的第一個值。之后,每一次的調用都會再一次運行你寫在函數(shù)里的循環(huán),然后返回下一個值,直到?jīng)]有值可以被返回。
一旦函數(shù)運行但是沒有再碰到y(tǒng)ield,生成器就被認為是空的。這可能是因為循環(huán)已經(jīng)結束了,或者因為不再滿足if/else條件了。
控制生成器耗盡
>>> class Bank(): # let's create a bank, building ATMs ... crisis = False ... def create_atm(self): ... while not self.crisis: ... yield "$100" >>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want >>> corner_street_atm = hsbc.create_atm() >>> print(corner_street_atm.next()) $100 >>> print(corner_street_atm.next()) $100 >>> print([corner_street_atm.next() for cash in range(5)]) ['$100', '$100', '$100', '$100', '$100'] >>> hsbc.crisis = True # crisis is coming, no more money! >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs >>> print(wall_street_atm.next()) <type 'exceptions.StopIteration'> >>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business >>> for cash in brand_new_atm: ... print cash $100 $100 $100 $100 $100 $100 $100 $100 $100 ...這個在很多方面都有用,比如控制對某個資源的訪問。
Itertools,好幫手
itertools模塊包含操作可迭代對象的特殊函數(shù)。想要復制一個生成器?連接兩個生成器?把嵌套列表中的數(shù)據(jù)整理到一個列表中?不創(chuàng)建另一個列表就直接Map/Zip?
那就import itertools。
例子?那我們看看4馬比賽中所有可能的到達順序(全排列):
>>> horses = [1, 2, 3, 4] >>> races = itertools.permutations(horses) >>> print(races) <itertools.permutations object at 0xb754f1dc> >>> print(list(itertools.permutations(horses))) [(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)]理解迭代的內部機制
迭代是一個暗含可迭代對象(實現(xiàn)了__iter__()方法)和迭代器(實現(xiàn)了__next__()方法)的過程。可迭代對象是任何你可以從中得到迭代器的對象。迭代器是可以讓你對可迭代對象進行迭代的對象。
更多信息請參考how does the for loop work。
轉載于:https://www.cnblogs.com/GuoYaxiang/p/6215685.html
總結
以上是生活随笔為你收集整理的Python中的生成器与yield的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: testuse备份
- 下一篇: mysq数据库再次理解