Python:生成器表达式与产量

Python:生成器表达式与产量,python,python-3.x,generator,yield,Python,Python 3.x,Generator,Yield,在Python中,通过生成器表达式创建生成器对象与使用yield语句创建生成器对象之间有什么区别吗 使用收益率: 使用生成器表达式: range_10_gen_expr = (x for x in range(10)) print(list(range_10_gen_expr)) print(list(range_10_gen_expr)) print(list(range_10_gen_expr)) 这两个函数都返回生成器对象,生成元组,例如(0,0)、(0,1)等 这两者有什么好处吗?想法

在Python中,通过生成器表达式创建生成器对象与使用yield语句创建生成器对象之间有什么区别吗

使用收益率:

使用生成器表达式:

range_10_gen_expr = (x for x in range(10))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))
这两个函数都返回生成器对象,生成元组,例如(0,0)、(0,1)等

这两者有什么好处吗?想法



谢谢大家!在这些答案中有很多很好的信息和进一步的参考资料

如果表达式比嵌套循环更复杂,那么使用
yield
是不错的选择。除此之外,还可以返回一个特殊的第一个值或特殊的最后一个值。考虑:

def Generator(x):
  for i in xrange(x):
    yield(i)
  yield(None)

在这个例子中,不是真的。但是
yield
可以用于更复杂的构造-它也可以接受调用者的值,并因此修改流。阅读更多细节(这是一个值得了解的有趣技巧)

无论如何,最好的建议是根据自己的需要使用更清晰的方法

这里有一个简单的协同程序示例,来自:


在考虑迭代器时,
itertools
模块:

。。。标准化了一组核心的快速、高效的工具,这些工具本身或组合使用都很有用。它们共同构成了一个“迭代器代数”,使得在纯Python中简洁高效地构造专用工具成为可能

性能,考虑

输入变量的笛卡尔积

等效于生成器表达式中的嵌套for循环。例如,
产品(A,B)
返回的值与
相同(A中x的(x,y)表示B中y的值)


在生成器表达式中可以容纳的简单循环类型没有区别。但是,可以使用yield创建执行更复杂处理的生成器。下面是一个生成斐波那契序列的简单示例:

>>> def fibgen():
...    a = b = 1
...    while True:
...        yield a
...        a, b = b, a+b

>>> list(itertools.takewhile((lambda x: x<100), fibgen()))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
def fibgen(): ... a=b=1 ... 尽管如此: ... 产生 ... a、 b=b,a+b
>>>列表(itertools.takewhile)((lambda x:x两者之间只有细微的区别。您可以使用
dis
模块自己检查这类事情

编辑:我的第一个版本在交互提示中反编译了在模块作用域中创建的生成器表达式。这与OP的版本稍有不同,OP在函数中使用了该表达式。我已对其进行了修改,以匹配问题中的实际情况

如下所示,“产量”生成器(第一种情况)在设置中有三条额外的指令,但与ITER的第一条指令相比,它们只在一个方面不同:“产量”该方法在循环内使用
LOAD\u FAST
代替
LOAD\u DEREF
LOAD\u DEREF
LOAD\u FAST
快,因此对于足够大的
x
(外循环)值,它使“屈服”版本比生成器表达式稍微快一点因为
y
的值在每次传递时加载速度稍快。对于
x
的较小值,由于设置代码的额外开销,加载速度稍慢

还值得指出的是,生成器表达式通常会在代码中内联使用,而不是像那样用函数包装。这将减少一点设置开销,并使生成器表达式在较小的循环值中保持稍快的速度,即使
LOAD\u FAST
给出了“屈服”否则,版本是一个优势

在这两种情况下,性能差异都不足以在两者之间做出决定。可读性更为重要,因此请根据当前情况选择最可读的

>>> def Generator(x, y):
...     for i in xrange(x):
...         for j in xrange(y):
...             yield(i, j)
...
>>> dis.dis(Generator)
  2           0 SETUP_LOOP              54 (to 57)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_FAST                0 (x)
              9 CALL_FUNCTION            1
             12 GET_ITER
        >>   13 FOR_ITER                40 (to 56)
             16 STORE_FAST               2 (i)

  3          19 SETUP_LOOP              31 (to 53)
             22 LOAD_GLOBAL              0 (xrange)
             25 LOAD_FAST                1 (y)
             28 CALL_FUNCTION            1
             31 GET_ITER
        >>   32 FOR_ITER                17 (to 52)
             35 STORE_FAST               3 (j)

  4          38 LOAD_FAST                2 (i)
             41 LOAD_FAST                3 (j)
             44 BUILD_TUPLE              2
             47 YIELD_VALUE
             48 POP_TOP
             49 JUMP_ABSOLUTE           32
        >>   52 POP_BLOCK
        >>   53 JUMP_ABSOLUTE           13
        >>   56 POP_BLOCK
        >>   57 LOAD_CONST               0 (None)
             60 RETURN_VALUE
>>> def Generator_expr(x, y):
...    return ((i, j) for i in xrange(x) for j in xrange(y))
...
>>> dis.dis(Generator_expr.func_code.co_consts[1])
  2           0 SETUP_LOOP              47 (to 50)
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                40 (to 49)
              9 STORE_FAST               1 (i)
             12 SETUP_LOOP              31 (to 46)
             15 LOAD_GLOBAL              0 (xrange)
             18 LOAD_DEREF               0 (y)
             21 CALL_FUNCTION            1
             24 GET_ITER
        >>   25 FOR_ITER                17 (to 45)
             28 STORE_FAST               2 (j)
             31 LOAD_FAST                1 (i)
             34 LOAD_FAST                2 (j)
             37 BUILD_TUPLE              2
             40 YIELD_VALUE
             41 POP_TOP
             42 JUMP_ABSOLUTE           25
        >>   45 POP_BLOCK
        >>   46 JUMP_ABSOLUTE            6
        >>   49 POP_BLOCK
        >>   50 LOAD_CONST               0 (None)
             53 RETURN_VALUE

在使用中,请注意生成器对象与生成器函数之间的区别

generator对象只使用一次,而generator函数可以在每次再次调用时重用,因为它返回一个新的generator对象

生成器表达式在实践中通常使用“raw”,而不将它们包装到函数中,并且它们返回生成器对象

例如:

与稍微不同的用法相比:

range_10_gen = range_10_gen_func()
print(list(range_10_gen))
print(list(range_10_gen))
print(list(range_10_gen))
哪些产出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[]
并与生成器表达式进行比较:

range_10_gen_expr = (x for x in range(10))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))
它还输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[]

在某些尚未指出的情况下,有一个差异可能很重要。使用
yield
可防止您将
return
用于其他用途

这意味着此代码的格式不正确(将其提供给解释器将给您一个
AttributeError
):

另一方面,此代码就像一个符咒:

class Tea:

    """With a cloud of milk, please"""

    def __init__(self, temperature):
        self.temperature = temperature

def mary_poppins_purse(tea_time=False):
    """I would like to make one thing clear: I never explain anything."""
    if tea_time:
        return Tea(355)
    else:
        return (item for item in ['lamp', 'mirror', 'coat rack',
                                  'tape measure', 'ficus'])

print(mary_poppins_purse(True).temperature)

是的,有区别

对于生成器表达式
(x表示expr中的var)
,在创建表达式时调用
iter(expr)

使用
def
yield
创建生成器时,如中所示:

def my_generator():
    for var in expr:
        yield x

g = my_generator()
iter(expr)
尚未调用。只有在
g
上迭代时才会调用它(可能根本不会调用)

以该迭代器为例:

from __future__ import print_function


class CountDown(object):
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        print("ITER")
        return self

    def __next__(self):
        if self.n == 0:
            raise StopIteration()
        self.n -= 1
        return self.n

    next = __next__  # for python2
此代码:

g1 = (i ** 2 for i in CountDown(3))  # immediately prints "ITER"
print("Go!")
for x in g1:
    print(x)
而:

def my_generator():
    for i in CountDown(3):
        yield i ** 2


g2 = my_generator()
print("Go!")
for x in g2:  # "ITER" is only printed here
    print(x)
由于大多数迭代器在
\uuu iter\uuu
中没有做很多事情,因此很容易忽略这种行为。一个真实的例子是Django的
QuerySet
,它和
data=(qs中x的f(x)
可能需要很多时间,而
def g():对于qs中的x:yield f(x)
后跟
data=g()
将立即返回


有关更多信息和正式定义,请参阅。

+1链接到David Beazley。他关于协同程序的演示是我很久以来读过的最令人兴奋的东西。可能没有他关于发电机的演示那么有用,但仍然很神奇。+1这太酷了……我还没见过这么简短而甜蜜的谎言没有递归的递归。非常简单的代码片段-我想Fibonacci会很高兴看到它的!!接受
class Tea:

    """With a cloud of milk, please"""

    def __init__(self, temperature):
        self.temperature = temperature

def mary_poppins_purse(tea_time=False):
    """I would like to make one thing clear: I never explain anything."""
    if tea_time:
        return Tea(355)
    else:
        return (item for item in ['lamp', 'mirror', 'coat rack',
                                  'tape measure', 'ficus'])

print(mary_poppins_purse(True).temperature)
def my_generator():
    for var in expr:
        yield x

g = my_generator()
from __future__ import print_function


class CountDown(object):
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        print("ITER")
        return self

    def __next__(self):
        if self.n == 0:
            raise StopIteration()
        self.n -= 1
        return self.n

    next = __next__  # for python2
g1 = (i ** 2 for i in CountDown(3))  # immediately prints "ITER"
print("Go!")
for x in g1:
    print(x)
def my_generator():
    for i in CountDown(3):
        yield i ** 2


g2 = my_generator()
print("Go!")
for x in g2:  # "ITER" is only printed here
    print(x)