【问题标题】:Generator expressions vs yield生成器表达式与产量
【发布时间】:2016-04-27 15:14:28
【问题描述】:

我正在观看此视频 (http://pyvideo.org/video/1758/loop-like-a-native-while-for-iterators-genera),最后他谈到了生成器表达式与正常生成器方式的相同之处,但情况似乎并非如此,以及我所讨论的其他主题阅读生成器表达式与产量说没有区别。但是,从我所看到的情况来看,每次生成器表达式不使用时,使用 yield 都会返回到 for 循环。它完成了它的任务,然后返回到 for 循环。 这可能是内存使用量的很大差异(取决于您循环的内容)对吧?我的想法是对的吗?

# generators call yield which will return to the loop it's called in before coming back
def evens(stream):
    for n in stream:
        if n % 2 == 0:
            print("Inside evens")
            yield n

# this is the same as above just a generator expression
def evens2(stream):
    print("Inside evens2")
    return (n for n in stream if n % 2 == 0)

【问题讨论】:

  • 您的print() 语句在evens 中的位置错误。它应该在开头,而不是在循环中。
  • 您的生成器表达式和生成器函数的行为方式完全相同。两者都只在迭代时产生n

标签: python generator yield generator-expression


【解决方案1】:

你的想法是错误的。您的生成器表达式与生成器函数完全相同执行相同的操作,只有一个区别:您将 print() 调用放置在错误的位置。在evens2 中,您打印生成器表达式执行之前,创建一个生成器对象,而在evens 中,您在生成器函数本身内部打印。

如果这是 Python 3(或者您使用了 from __future__ import print_function),您也可以在生成器表达式中使用 print() 函数:

def evens2(stream):
    return (print('inside evens2') or n for n in stream if n % 2 == 0)

这相当于:

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield print("Inside evens") or n

print() 总是返回None,所以print(..) or n 将返回n。对其中任何一个的迭代都将打印并产生所有甚至 n 值。

演示:

>>> def evens2(stream):
...     return (print('inside evens2') or n for n in stream if n % 2 == 0)
...
>>> def evens(stream):
...     for n in stream:
...         if n % 2 == 0:
...             yield print("Inside evens") or n
...
>>> g1 = evens([1, 2, 3, 4, 5])
>>> g2 = evens2([1, 2, 3, 4, 5])
>>> g1
<generator object evens at 0x10bbf5938>
>>> g2
<generator object evens2.<locals>.<genexpr> at 0x10bbf5570>
>>> next(g1)
Inside evens
2
>>> next(g2)
inside evens2
2
>>> next(g1)
Inside evens
4
>>> next(g2)
inside evens2
4

这两个调用都会生成一个生成器对象,并且每次使用next() 将它们推进一步时,两个生成器对象都会打印额外的信息。

就 Python 而言,两个生成器对象产生或多或少相同的字节码:

>>> import dis
>>> dis.dis(compile('(n for n in stream if n % 2 == 0)', '', 'exec').co_consts[0])
  1           0 LOAD_FAST                0 (.0)
        >>    3 FOR_ITER                27 (to 33)
              6 STORE_FAST               1 (n)
              9 LOAD_FAST                1 (n)
             12 LOAD_CONST               0 (2)
             15 BINARY_MODULO
             16 LOAD_CONST               1 (0)
             19 COMPARE_OP               2 (==)
             22 POP_JUMP_IF_FALSE        3
             25 LOAD_FAST                1 (n)
             28 YIELD_VALUE
             29 POP_TOP
             30 JUMP_ABSOLUTE            3
        >>   33 LOAD_CONST               2 (None)
             36 RETURN_VALUE
>>> dis.dis(compile('''\
... def evens(stream):
...     for n in stream:
...         if n % 2 == 0:
...             yield n
... ''', '', 'exec').co_consts[0])
  2           0 SETUP_LOOP              35 (to 38)
              3 LOAD_FAST                0 (stream)
              6 GET_ITER
        >>    7 FOR_ITER                27 (to 37)
             10 STORE_FAST               1 (n)

  3          13 LOAD_FAST                1 (n)
             16 LOAD_CONST               1 (2)
             19 BINARY_MODULO
             20 LOAD_CONST               2 (0)
             23 COMPARE_OP               2 (==)
             26 POP_JUMP_IF_FALSE        7

  4          29 LOAD_FAST                1 (n)
             32 YIELD_VALUE
             33 POP_TOP
             34 JUMP_ABSOLUTE            7
        >>   37 POP_BLOCK
        >>   38 LOAD_CONST               0 (None)
             41 RETURN_VALUE

两者都使用FOR_ITER循环,COMPARE_OP查看BINARY_MODULO的输出是否等于0,都使用YIELD_VALUE产生n的值

【讨论】:

  • 解释。我不确定我是否可以在此处放置打印声明。所以生成器表达式只是对我们隐藏了 yield 语句。
  • @user441521:就像列表推导隐藏了我们的 list.append() 调用一样。
猜你喜欢
  • 2011-01-01
  • 2013-05-22
  • 2018-05-02
  • 2023-02-02
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
  • 2017-12-08
  • 2016-04-27
相关资源
最近更新 更多