成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python yield用法淺析(stackoverflow)

Loong_T / 1630人閱讀

摘要:生成器生成器是迭代器,但是只能迭代一次,生成器不會將所有值存儲在內(nèi)存中,而是實時的生成這些值看上去除了用替換了原來的外,它們沒什么不同。

這是stackoverflow上一個關(guān)于python中yield用法的帖子,這里翻譯自投票最高的一個回答,原文鏈接 here

問題

Python中yield關(guān)鍵字的用途是什么?它有什么作用?
例如,我試圖理解以下代碼 ¹:

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild 

這是調(diào)用者(caller):

result, candidates = [], [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

當(dāng)調(diào)用方法_get_child_candidates時會發(fā)生什么?返回了一個列表(list)?還是返回了一個元素?然后被重復(fù)調(diào)用了嗎?調(diào)用何時結(jié)束?

¹ :代碼來自 Jochen Schulz (jrschulz), who made a great Python library for metric spaces. 這是完整源代碼的鏈接:Module mspace.

回答

要想理解yield的作用,你必須了解什么是生成器(generators),在這之前,我們先來看可迭代對象(iterables)。

可迭代對象 (iterables)

當(dāng)你創(chuàng)建了一個列表,你可以遍歷這個列表讀取它的每一個元素,逐個讀取列表元素稱為迭代(iteration)。

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylist就是一個可迭代對象(iterable)。當(dāng)你使用列表生成式(list comprehension)創(chuàng)建一個列表(list),即創(chuàng)建了一個可迭代對象。

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

可以使用for... in...的所有對象都是可迭代對象:列表(lists)、字符串、文件...
這些可迭代對象使用很方便,因為你可以根據(jù)需要如你所愿的讀取其中的元素。但是,當(dāng)你有大量數(shù)據(jù)時把所有值都存儲在內(nèi)存中,這樣往往不是你想要的( but you store all the values in memory and this is not always what you want when you have a lot of values.)。

生成器 (Generators)

生成器是迭代器(iterators),但是只能迭代一次,生成器不會將所有值存儲在內(nèi)存中,而是實時的生成這些值:

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

看上去除了用()替換了原來的[]外,它們沒什么不同。但是,你不可以再次使用for i in mygenerator ,因為生成器只能被迭代一次:計算出0,然后并不保存結(jié)果和狀態(tài)繼續(xù)計算出1,最后計算出4,逐一生成。

yield

yield 是一個類似 return 的關(guān)鍵字,不同的是這個函數(shù)將返回一個生成器。

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!

>>> for i in mygenerator:
...     print(i)
0
1
4

這個例子沒有什么實際作用。但是當(dāng)你知道你的函數(shù)將返回大量你只需要讀取一次的值時,使用生成器是一個有效的做法。
要掌握 yeild,你必須要知道當(dāng)你調(diào)用這個函數(shù)時,你在函數(shù)體中編寫的代碼并沒有立馬執(zhí)行。
該函數(shù)僅僅返回一個生成器對象,這有點(diǎn)棘手 :-)

然后,你的代碼將從for循環(huán)每次使用生成器停止的位置繼續(xù)執(zhí)行。

現(xiàn)在到了關(guān)鍵部分:

for第一次調(diào)用從函數(shù)創(chuàng)建的生成器對象,函數(shù)將從頭開始執(zhí)行直到遇到yeild,然后返回yield后的值作為第一次迭代的返回值。接下來每次調(diào)用都會再次執(zhí)行你在函數(shù)中定義的循環(huán),并返回(return)下一個值,直到?jīng)]有值可以返回(return)。

當(dāng)循環(huán)結(jié)束,或者不滿足if/else條件,導(dǎo)致函數(shù)運(yùn)行但不會執(zhí)行(not hit)yeild,此時生成器被認(rèn)為是空的。

問題代碼的解釋 (Your code explained)

生成器 (Generator):

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):

    # Here is the code that will be called each time you use the generator object:

    # If there is still a child of the node object on its left
    # AND if distance is ok, return the next child
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild

    # If there is still a child of the node object on its right
    # AND if distance is ok, return the next child
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild

    # If the function arrives here, the generator will be considered empty
    # there is no more than two values: the left and the right children

調(diào)用者 (Caller):

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidates list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

這段代碼包含幾個高明的部分:

這個循環(huán)對列表進(jìn)行迭代,但是迭代中列表還在不斷擴(kuò)展 :-) 這是一種遍歷嵌套數(shù)據(jù)的簡明方法,即使這樣有些危險,因為你可能會陷入死循環(huán)中。在這個例子中,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))窮盡了生成器產(chǎn)生的所有值,但while不斷的創(chuàng)建新的生成器對象加入到列表,因為每個對象作用在不同節(jié)點(diǎn)上,所以每個生成器都將生成不同的值。

extend()是一個列表(list)對象的方法,作用于可迭代對象(iterable),并將其值添加到列表里。

通常,通常我們將列表作為參數(shù)傳遞給它:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

但是在你的代碼里它接收到的是一個生成器(generator),這很好,因為:

你不必重復(fù)讀取這些值

你可以有很多子對象,但不需要將它們都存儲在內(nèi)存里。

它很有效,因為Python不關(guān)心一個方法的參數(shù)是否是列表,Python只希望他是一個可迭代對象,所以這個參數(shù)可以是列表,元組,字符串和生成器!這就是所謂的duck typing ,這也是Python為何如此酷的原因之一,但這已經(jīng)是另外一個問題了......

你可以在這里停下,來看一些生成器的高級用法:

控制生成器的窮盡 (Controlling a generator exhaustion)
>>> 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())

>>> wall_street_atm = hsbc.create_atm() # It"s even true for new ATMs
>>> print(wall_street_atm.next())

>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())

>>> 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
...

注意,對于Python 3,請使用 print(corner_street_atm.__next__()) 或者 print(next(corner_street_atm))

這在很多場景都非常有用,例如控制資源的獲取。

Itertools,你最好的朋友 (Itertools, your best friend)

itertools模塊包含很多處理可迭代對象的特殊方法。曾經(jīng)想要復(fù)制一個生成器嗎?連接兩個生成器?用一行代碼將嵌套列表中的值進(jìn)行分組?不創(chuàng)建另一個列表進(jìn)行Map/Zip?

只需要import itertools

需要一個例子?讓我們來看看4匹馬賽跑到達(dá)終點(diǎn)先后順序的所有可能情況:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)

>>> 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)]
了解迭代的內(nèi)部機(jī)制 (Understanding the inner mechanisms of iteration)

迭代是一個實現(xiàn)可迭代對象(實現(xiàn)的是 __iter__() 方法)和迭代器(實現(xiàn)的是 __next__() 方法)的過程。你可以獲取一個迭代器的任何對象都是可迭代對象,迭代器可以讓你迭代遍歷一個可迭代對象(Iterators are objects that let you iterate on iterables.) .

在這篇文章中有關(guān)于for循環(huán)如何工作的更多信息:here

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/42825.html

相關(guān)文章

  • sum() 函數(shù)性能堪憂,列表降維有何良方?

    摘要:在上一篇如何給列表降維函數(shù)的妙用中,我們介紹了這個用法,還對函數(shù)做了擴(kuò)展的學(xué)習(xí)。是的,函數(shù)做列表降維有奇效,但它性能堪憂,并不是最好的選擇。這正是函數(shù)出于一致性考慮,而舍棄掉的實現(xiàn)方案。 showImg(https://segmentfault.com/img/remote/1460000019004608?w=5184&h=2916); 本文原創(chuàng)并首發(fā)于公眾號【Python貓】,未經(jīng)...

    李濤 評論0 收藏0
  • generator探幽(1)--koa中間件機(jī)制淺析

    摘要:當(dāng)運(yùn)行到時,不會暫停,而是直接跳進(jìn)函數(shù)執(zhí)行函數(shù)內(nèi)的代碼。由于函數(shù)中沒有,因此會一直執(zhí)行完函數(shù)中的代碼,并返回至函數(shù)中執(zhí)行后面的代碼。 本系列旨在通過對co,koa等庫源碼的研究,進(jìn)而理解generator在異步編程中的重大作用(ps:所有代碼請在node --harmony或者iojs環(huán)境中運(yùn)行) koa中間件的形式 相信用過koa的小伙伴一定很熟悉下面這段代碼 var app ...

    Jensen 評論0 收藏0
  • 切圖崽的自我修養(yǎng)-[ES6] 生成器Generator淺析

    摘要:搞這么神秘其實就是個迭代器的核心實際上就是一個,通過關(guān)鍵字能夠把函數(shù)體拆成完全可控執(zhí)行片段,在函數(shù)體外部通過來對這些執(zhí)行片段進(jìn)行遍歷這和遍歷這些數(shù)據(jù)結(jié)構(gòu)是一個道理只不過用來遍歷函數(shù)片段,而用來遍歷元素對生成器執(zhí)行操作,進(jìn)行生成器的入口開始執(zhí) Generator 搞這么神秘 其實就是個迭代器 Generator的核心實際上就是一個Iterator,通過yield關(guān)鍵字能夠把函數(shù)體拆成完全...

    Dogee 評論0 收藏0
  • 切圖崽的自我修養(yǎng)-[ES6] 生成器Generator淺析

    摘要:搞這么神秘其實就是個迭代器的核心實際上就是一個,通過關(guān)鍵字能夠把函數(shù)體拆成完全可控執(zhí)行片段,在函數(shù)體外部通過來對這些執(zhí)行片段進(jìn)行遍歷這和遍歷這些數(shù)據(jù)結(jié)構(gòu)是一個道理只不過用來遍歷函數(shù)片段,而用來遍歷元素對生成器執(zhí)行操作,進(jìn)行生成器的入口開始執(zhí) Generator 搞這么神秘 其實就是個迭代器 Generator的核心實際上就是一個Iterator,通過yield關(guān)鍵字能夠把函數(shù)體拆成完全...

    李義 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<