我正在学习这个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不是空的。
答案 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
注意:如果您不将ATM
与iter
Sentinel参数一起使用,'crisis!'
对象永远不会用完!
infinite_atm_iterator = iter(ATM(some_bank))