Return 或从调用生成器的函数中产生?
Return or yield from a function that calls a generator?
我有一个生成器 generator
以及一个方便的方法 - generate_all
.
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
应该generate_all
return
还是yield
?我希望这两种方法的用户使用相同的方法,即
for x in generate_all()
应该等于
some_list = get_the_list()
for x in generate(some_list)
您可能正在寻找 Generator Delegation (PEP380)
For simple iterators, yield from iterable
is essentially just a shortened form of for item in iterable: yield item
def generator(iterable):
for i in iterable:
yield do_something(i)
def generate_all():
yield from generator(get_the_list())
它非常简洁,还有许多其他优点,例如能够链接 arbitrary/different 可迭代对象!
return generator(list)
为所欲为。但请注意
yield from generator(list)
是等效的,但在 generator
耗尽后有机会产生更多的值。例如:
def generator_all_and_then_some():
list = get_the_list()
yield from generator(list)
yield "one last thing"
在这种特殊情况下,以下两个语句在功能上似乎是等效的:
return generator(list)
和
yield from generator(list)
后面跟
差不多
for i in generator(list):
yield i
return
声明returns你要找的生成器。 yield from
或 yield
语句将您的整个函数变成 returns 生成器,它通过您正在寻找的那个。
从用户的角度来看,没有区别。然而,在内部,return
可以说更有效,因为它不会将 generator(list)
包装在多余的直通生成器中。如果您计划对包装生成器的元素进行 any 处理,当然可以使用某种形式的 yield
。
你会 return
它。
yield
ing* 会导致 generate_all()
评估生成器本身,并且在该外部生成器上调用 next
会 return 内部生成器 return 由第一个函数编辑,这不是您想要的。
*
不包括 yield from
生成器是惰性求值的,因此 return
或 yield
在调试代码或抛出异常时会有不同的行为。
使用 return
时,您的 generator
中发生的任何异常都不会知道 generate_all
,这是因为当 generator
真正执行时,您已经离开了generate_all
函数。在 yield
的情况下,回溯中将包含 generate_all
。
def generator(some_list):
for i in some_list:
raise Exception('exception happened :-)')
yield i
def generate_all():
some_list = [1,2,3]
return generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
8 return generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-3-b19085eab3e1> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
如果它使用 yield from
:
def generate_all():
some_list = [1,2,3]
yield from generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
8 yield from generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-4-be322887df35> in generate_all()
6 def generate_all():
7 some_list = [1,2,3]
----> 8 yield from generator(some_list)
9
10 for item in generate_all():
<ipython-input-4-be322887df35> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
然而,这是以性能为代价的。额外的生成器层确实有一些开销。所以 return
通常会比 yield from ...
(或 for item in ...: yield item
)快一点。在大多数情况下,这无关紧要,因为无论您在生成器中做什么,通常都会支配 运行 时间,因此附加层不会引人注意。
然而 yield
有一些额外的优势:你不局限于单个迭代器,你还可以轻松地产生额外的项目:
def generator(some_list):
for i in some_list:
yield i
def generate_all():
some_list = [1,2,3]
yield 'start'
yield from generator(some_list)
yield 'end'
for item in generate_all():
print(item)
start
1
2
3
end
在您的情况下,操作非常简单,我不知道是否有必要为此创建多个函数,可以轻松地使用内置 map
或生成器表达式来代替:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
两者应该是相同的(除了发生异常时的一些差异)以供使用。如果他们需要一个更具描述性的名称,那么您仍然可以将它们包装在一个函数中。
有多个帮助程序可以在内置的可迭代对象上包装非常常见的操作,并且可以在内置的 itertools
模块中找到更多的帮助程序。在这种简单的情况下,我会简单地求助于这些,并且仅在非平凡的情况下编写您自己的生成器。
但我假设您的实际代码更复杂,因此可能不适用,但我认为如果不提及替代方案,这将不是一个完整的答案。
我有一个生成器 generator
以及一个方便的方法 - generate_all
.
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
应该generate_all
return
还是yield
?我希望这两种方法的用户使用相同的方法,即
for x in generate_all()
应该等于
some_list = get_the_list()
for x in generate(some_list)
您可能正在寻找 Generator Delegation (PEP380)
For simple iterators,
yield from iterable
is essentially just a shortened form offor item in iterable: yield item
def generator(iterable):
for i in iterable:
yield do_something(i)
def generate_all():
yield from generator(get_the_list())
它非常简洁,还有许多其他优点,例如能够链接 arbitrary/different 可迭代对象!
return generator(list)
为所欲为。但请注意
yield from generator(list)
是等效的,但在 generator
耗尽后有机会产生更多的值。例如:
def generator_all_and_then_some():
list = get_the_list()
yield from generator(list)
yield "one last thing"
在这种特殊情况下,以下两个语句在功能上似乎是等效的:
return generator(list)
和
yield from generator(list)
后面跟
差不多for i in generator(list):
yield i
return
声明returns你要找的生成器。 yield from
或 yield
语句将您的整个函数变成 returns 生成器,它通过您正在寻找的那个。
从用户的角度来看,没有区别。然而,在内部,return
可以说更有效,因为它不会将 generator(list)
包装在多余的直通生成器中。如果您计划对包装生成器的元素进行 any 处理,当然可以使用某种形式的 yield
。
你会 return
它。
yield
ing* 会导致 generate_all()
评估生成器本身,并且在该外部生成器上调用 next
会 return 内部生成器 return 由第一个函数编辑,这不是您想要的。
*
不包括 yield from
生成器是惰性求值的,因此 return
或 yield
在调试代码或抛出异常时会有不同的行为。
使用 return
时,您的 generator
中发生的任何异常都不会知道 generate_all
,这是因为当 generator
真正执行时,您已经离开了generate_all
函数。在 yield
的情况下,回溯中将包含 generate_all
。
def generator(some_list):
for i in some_list:
raise Exception('exception happened :-)')
yield i
def generate_all():
some_list = [1,2,3]
return generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
8 return generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-3-b19085eab3e1> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
如果它使用 yield from
:
def generate_all():
some_list = [1,2,3]
yield from generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
8 yield from generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-4-be322887df35> in generate_all()
6 def generate_all():
7 some_list = [1,2,3]
----> 8 yield from generator(some_list)
9
10 for item in generate_all():
<ipython-input-4-be322887df35> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
然而,这是以性能为代价的。额外的生成器层确实有一些开销。所以 return
通常会比 yield from ...
(或 for item in ...: yield item
)快一点。在大多数情况下,这无关紧要,因为无论您在生成器中做什么,通常都会支配 运行 时间,因此附加层不会引人注意。
然而 yield
有一些额外的优势:你不局限于单个迭代器,你还可以轻松地产生额外的项目:
def generator(some_list):
for i in some_list:
yield i
def generate_all():
some_list = [1,2,3]
yield 'start'
yield from generator(some_list)
yield 'end'
for item in generate_all():
print(item)
start
1
2
3
end
在您的情况下,操作非常简单,我不知道是否有必要为此创建多个函数,可以轻松地使用内置 map
或生成器表达式来代替:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
两者应该是相同的(除了发生异常时的一些差异)以供使用。如果他们需要一个更具描述性的名称,那么您仍然可以将它们包装在一个函数中。
有多个帮助程序可以在内置的可迭代对象上包装非常常见的操作,并且可以在内置的 itertools
模块中找到更多的帮助程序。在这种简单的情况下,我会简单地求助于这些,并且仅在非平凡的情况下编写您自己的生成器。
但我假设您的实际代码更复杂,因此可能不适用,但我认为如果不提及替代方案,这将不是一个完整的答案。