ziy*_*ang 6 python iteration yield generator
我将函数定义f为
def f(flag):
n = 10
if flag:
for i in range(n):
yield i
else:
return range(n)
Run Code Online (Sandbox Code Playgroud)
但f无论如何返回发电机flag:
>>> f(True)
<generator object f at 0x0000000003C5EEA0>
>>> f(False)
<generator object f at 0x0000000007AC4828>
Run Code Online (Sandbox Code Playgroud)
如果我遍历返回的对象:
# prints normally
for i in f(True):
print(i)
# doesn't print
for i in f(False):
print(i)
Run Code Online (Sandbox Code Playgroud)
看起来像f(False)返回已经迭代的生成器.什么原因?谢谢.
Mar*_*ers 12
包含yield语句的函数始终返回生成器对象.
只有当您遍历该生成器对象时,才会执行该函数中的代码.在那之前,函数中没有代码被执行,Python 也不知道你只会返回.
注意,return在生成器函数中使用具有与常规函数不同的语义 ; return在这种情况下,简单地被视为"退出发电机"; 返回值被丢弃,因为生成器只能通过yield表达式生成值.
看起来你想要使用yield from:
def f(flag):
n = 10
if flag:
for i in range(n):
yield i
else:
yield from range(n)
Run Code Online (Sandbox Code Playgroud)
yield from 需要Python 3.3或更高版本.
请参阅yield表达式文档:
yield在函数体中使用表达式会使该函数成为生成器.当调用生成器函数时,它返回一个称为生成器的迭代器.然后该生成器控制生成器函数的执行.当调用其中一个生成器的方法时,执行开始.那时,执行继续执行第一个
yield表达式,再次暂停它,将expression_list的值返回给生成器的调用者.
对生成器的迭代调用generator.__next__()方法,触发执行.
如果您想在某些时候返回生成器,则不要yield在此函数中使用.你可以通过其他方式生产发电机; 例如,使用单独的函数,或者使用生成器表达式:
def f(flag):
n = 10
if flag:
return (i for i in range(n))
else:
return range(n)
Run Code Online (Sandbox Code Playgroud)
现在没有yield使用f它,它将不再直接生成生成器对象.相反,生成器表达式(i for i in range(n))会生成它,但只能有条件地生成它.
您可以使用实际使用的嵌套函数来解决此问题yield:
def f(flag):
def gen():
for i in range(n):
yield i
n = 10
if flag:
return gen()
else:
return range(n)
>>> f(True)
<generator object gen at 0x7f62017e3730>
>>> f(False)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)
正如Martijn指出的那样,任何包含a的函数yield都将始终返回一个生成器对象,因此,如果在某些情况下,您希望在调用主体时f实际执行该主体f(),而不是仅在迭代时才执行,则必须使用此方法。
标准库实例方法map从concurrent.Futures.ProcessPoolExecutor/ concurrent.Futures.ThreadPoolExecutor使用它来确保期货提交,一旦map被调用,而不是只有当你尝试实际从中检索的结果,例如:
def map(self, fn, *iterables, timeout=None):
if timeout is not None:
end_time = timeout + time.time()
fs = [self.submit(fn, *args) for args in zip(*iterables)]
# Yield must be hidden in closure so that the futures are submitted
# before the first iterator value is required.
def result_iterator():
try:
for future in fs:
if timeout is None:
yield future.result()
else:
yield future.result(end_time - time.time())
finally:
for future in fs:
future.cancel()
return result_iterator()
Run Code Online (Sandbox Code Playgroud)