从调用生成器的函数返回或产生?

hya*_*kov 31 python yield return function 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?
Run Code Online (Sandbox Code Playgroud)

应该generate_all return还是yield?我希望两种方法的用户都使用相同的方法,即

for x in generate_all()
Run Code Online (Sandbox Code Playgroud)

应该等于

some_list = get_the_list()
for x in generate(some_list)
Run Code Online (Sandbox Code Playgroud)

ti7*_*ti7 18

您可能正在寻找生成器委托 (PEP380)

对于简单的迭代器,yield from iterable本质上只是一种缩写形式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())
Run Code Online (Sandbox Code Playgroud)

它非常简洁,还有许多其他优点,例如能够链接任意/不同的迭代!

  • 仅当您自己正在执行除返回新生成器之外的其他操作时,才需要委托。如果您只是返回新的生成器,则不需要委托。所以“yield from”是没有意义的,除非你的包装器做了*一些*其他的生成器-y。 (3认同)
  • 第一个也可以是单行句:)。`yield from map(do_something, iterable)` 甚至 `yield from (do_something(x) for x in iterable)` (2认同)

che*_*ner 15

return generator(list)做你想做的。但请注意

yield from generator(list)
Run Code Online (Sandbox Code Playgroud)

将是等效的,但有机会在generator用尽后产生更多值。例如:

def generator_all_and_then_some():
    list = get_the_list()
    yield from generator(list)
    yield "one last thing"
Run Code Online (Sandbox Code Playgroud)

  • 我相信,当生成器的使用者在其中“抛出”异常时,以及受堆栈跟踪影响的其他操作时,“yield from”和“return”之间存在细微的差别。 (5认同)

MSe*_*ert 13

发电机是懒惰的,所以评估returnyield将表现不同,当你调试代码,或者抛出一个异常。

随着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():
    ...
Run Code Online (Sandbox Code Playgroud)
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():
    ...
Run Code Online (Sandbox Code Playgroud)

如果它使用yield from

def generate_all():
    some_list = [1,2,3]
    yield from generator(some_list)

for item in generate_all():
    ...
Run Code Online (Sandbox Code Playgroud)
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 :-)
Run Code Online (Sandbox Code Playgroud)

然而,这是以性能为代价的。额外的生成器层确实有一些开销。所以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)
Run Code Online (Sandbox Code Playgroud)
def generate_all():
    some_list = [1,2,3]
    yield from generator(some_list)

for item in generate_all():
    ...
Run Code Online (Sandbox Code Playgroud)

在您的情况下,操作非常简单,我不知道是否有必要为此创建多个函数,您可以轻松地使用内置map表达式或生成器表达式:

map(do_something, get_the_list())          # map
(do_something(i) for i in get_the_list())  # generator expression
Run Code Online (Sandbox Code Playgroud)

两者应该是相同的(除了发生异常时的一些差异)才能使用。如果他们需要一个更具描述性的名称,那么您仍然可以将它们包装在一个函数中。

有多个助手可以在内置的可迭代对象上包装非常常见的操作,更多的可以在内置itertools模块中找到。在这种简单的情况下,我只会求助于这些,并且仅在非平凡的情况下编写您自己的生成器。

但我认为您的真实代码更复杂,因此可能不适用,但我认为如果不提及替代方案,它就不会是一个完整的答案。


Mad*_*ist 10

在这种特殊情况下,以下两个语句在功能上是等效的:

return generator(list)
Run Code Online (Sandbox Code Playgroud)

yield from generator(list)
Run Code Online (Sandbox Code Playgroud)

后者大致相同

for i in generator(list):
    yield i
Run Code Online (Sandbox Code Playgroud)

return语句返回您正在寻找的生成器。一个yield fromyield声明将你的整体功能到的东西,返回一个生成器,它通过你正在寻找的人。

从用户的角度来看,没有区别。然而,在内部,return它可以说更有效,因为它没有包含generator(list)在一个多余的直通发生器中。如果您打算对包装生成器的元素进行任何处理yield,当然可以使用某种形式。