类中的python生成器

时间:2016-05-31 02:18:38

标签: python class generator

我正在学习这个thread的生成器。这对发电机来说确实是一个很好的例子。但我对其中一个示例代码感到困惑。

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

hsbc.crisis 重置为 False 时, corner_steet_atm 除了StopIteration之外不能产生任何效果。 为什么会这样呢?我认为危机后corner_street_atm不是空的。

3 个答案:

答案 0 :(得分:2)

问题来自于当你说

时的事实
corner_street_atm = hsbc.create_atm()

您正在调用该函数一次,这就是它将被调用的所有时间。因此,让我们一起了解正在发生的事情

  def create_atm(self):
        # Called right away, when you say the above line and only then
        print("Called function") 

        # Loops while crisis = False
        while not self.crisis: 
            yield "$100"

现在,我们可以问一下当危机是真的时我们的方法在哪里看的问题,我们发现它在下面显示的位置是点:

  def create_atm(self):
        # Called right away, when you say the above line and only then
        print("Called function") 

        # Loops while crisis = False
        while not self.crisis: 
            yield "$100"
            #<------ Where you function points after you set crisis to false and get the next

嗯,那里什么都没有,所以你得到一个错误。

你真正想要的似乎是atm的无限循环。像这样:

class Bank():
    crisis = False

    def create_atm(self):
        print("Called function")
        while True:
            if not self.crisis:
                yield "$100"
            else:
                yield "$0"

小心这一点,作为你的最后一次通话

for cash in brand_new_atm:
    print(cash)

将永远循环,因为您目前看不到自动取款机中有多少现金(因此,只要不存在危机,它就会发出美元钞票。

答案 1 :(得分:1)

通常,这是因为您在设置corner_street_atm.next()后调用了hsbc.crisis=True。如果你这样做,你就没有问题:

hsbc.crisis=False

corner_street_atm = hsbc.create_atm()

hsbc.crisis=True

hsbc.crisis=False

corner_street_atm.next()
Out[54]: '$100'

原因是corner_street_atm是一个生成器(迭代器)对象,并且一旦在这种情况下创建的生成器将调用o,对于next()方法的每次调用都没有参数;如果返回的值等于sentinel,则会引发StopIteration,否则将返回值 [1]

事情是,一旦你在corner_street_atm.next()之后调用hsbc.crisis=True,就会引发异常。一旦引发异常,就无法再循环此对象,除非您创建新对象!

答案 2 :(得分:1)

这里的问题是你的生成器(它是一种迭代器)已经用尽了。一旦它耗尽,迭代器就不应该在产生值和提升StopIteration之间来回翻转(换句话说,即使它不是你想要它做的,你的例子也应该表现得如此)。以下是有关迭代器协议的更多信息:

https://docs.python.org/3/tutorial/classes.html#iterators

在这种特定情况下,生成器停止产生值的原因是,一旦退出while循环(当next为True时调用'crisis'时就会发生这种情况)无法重新进入循环。

您最有可能想要做的是在单独的__iter__对象上使用ATM魔术方法。更多关于这一点:

https://docs.python.org/3/library/functions.html#iter

它可能看起来像这样:

class ATM():
    def __init__(self, owner):
        self.owner = owner
    def __iter__(self):
        yield "$100"
    def __call__(self):
        if self.owner.crisis is True:
            return 'crisis!'

class Bank(): # let's create a bank, building ATMs
    crisis = False
    def create_atm(self):
        return ATM(self)

你想以这种方式得到一个atm迭代器:

>>> hsbc = Bank() 
>>> corner_street_atm = hsbc.create_atm() # this way
>>> corner_street_atm = ATM(hsbc) # or this way
>>> corner_street_atm_iterator = iter(corner_street_atm, 'crisis!')

最后一行使用iter函数的可选形式,因为提供了第二个参数。如果StopIteration返回提供给corner_street_atm.__call__()的sentinel值,则第二个表单返回的迭代器会引发iter。在这种情况下,标记是'crisis!'

并像这样迭代:

>>> print(corner_street_atm_iterator.next())  
$100  

注意:如果您不将ATMiter Sentinel参数一起使用,'crisis!'对象永远不会用完!

infinite_atm_iterator = iter(ATM(some_bank))