生成器表达式使用生成器创建后分配的列表

时间:2018-10-24 11:52:06

标签: python expression generator

我找到了这个示例,但我不明白为什么它无法正常工作? 我以为它必须输出[1, 8, 15][2, 8, 22]

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
print(list(g))


>>>[8]

5 个答案:

答案 0 :(得分:17)

原因是,在创建时,生成器(a for b in c if d)仅评估c(有时有时使b可预测)。但是abd是在消耗时间(每次迭代)进行评估的。此处,在评估arrayd)时,它使用了来自封闭范围的array.count(x) > 0 current 绑定。

例如,您可以这样做:

g = (x for x in [] if a)

没有事先声明a。但是,必须确保a在生成器被使用时存在。

但是您不能做类似的事情:

g = (x for x in a if True)

根据要求:

您可以使用常见的生成器功能观察相似(但不完全相同)的模式:

def yielder():
    for x in array:
        if array.count(x) > 0:
            yield x

array = [1, 8, 15]
y = yielder()
array = [2, 8, 22]
list(y)
# [2, 8, 22]

生成器函数在消耗之前不会执行任何主体。因此,即使for循环标头中的array也绑定得晚。甚至还有一个更令人不安的示例,其中我们在迭代过程中“切换” array

array = [1, 8, 15]
y = yielder()
next(y)
# 1
array = [3, 7]
next(y)  # still iterating [1, 8, 15], but evaluating condition on [3, 7]
# StopIteration raised

答案 1 :(得分:6)

摘自Generator expressions上的文档:

  

生成器表达式中使用的变量惰性计算   __next__()方法是为生成器对象调用的(在同一方法中   像普通发电机一样流行)。但是,   最左边的for子句会被立即评估,因此会出现错误   由它产生的光会在产生器的位置发射   定义了表达式,而不是在第一个值的位置   被检索。

所以当你跑步

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)

仅评估生成器表达式中的第一个arrayxarray.count(x)仅在您呼叫next(g)时被评估。由于您使array指向另一个列表[2, 8, 22] 使用之前,您将获得“意外的”结果。

array = [2, 8, 22]
print(list(g))  # [8]

答案 2 :(得分:1)

第一次创建数组并分配数组中的元素时,数组的元素指向某个内存位置,并且生成器会保留该位置(而不是数组的位置)以供执行。

但是,当您修改数组的元素时,它会更改,但是由于'8'对它们两者都是通用的,因此python不会重新分配它,并且在修改后指向同一元素。

请看下面的示例以更好地理解

array = [1, 8, 15]
for i in array:
    print(id(i))

g = (x for x in array if array.count(x) > 0)

print('<======>')

array = [2, 8, 22]
for i in array:
    print(id(i))

print(array)
print(list(g))

输出

140208067495680
140208067495904
140208067496128
<======>
140208067495712
140208067495904 # memory location is still same
140208067496352
[2, 8, 22]
[8]

答案 3 :(得分:0)

实际上,如果您仔细看,这并不是真的很疯狂。 看

g = (x for x in array if array.count(x) > 0)

它将创建一个生成器,该生成器将遍历数组并搜索已经存在的值的计数是否大于零。因此,生成器仅查找1815,并且当您将值更改为另一个值时,生成器将再次查找先前的值,而不是新的值。因为它(生成器)在数组拥有它们时创建。

因此,如果将数千个值放在数组中,则仅查找这三个值。

答案 4 :(得分:0)

混乱和答案就在这行中:g = (x for x in array if array.count(x) > 0)
如果我们简化此行,则它将变为:g = (x for x in array1 if array2.count(x) > 0)

现在,当创建 generator 时,它将保留array1对象的引用。因此,即使我将array1的值更改为任何其他值(即将其设置为新的数组对象),也不会影响 generator 的{{1} }。因为只有array1在更改,所以它是对象引用。但是array1是动态检查的。因此,如果我们更改其值,它将得到反映。

您可以从以下代码中看到输出,以了解其击球员。看到working online here

array2

输出:

array1 = [1, 8, 15] #Set value of `array1`
array2 = [2, 3, 4, 5, 8] #Set value of `array2`
print("Old `array1` object ID: " + repr(id(array1)))
print("Old `array2` object ID: " + repr(id(array2)))
g = (x for x in array1 if array2.count(x) > 0)
array1 = [0, 9] #Changed value of `array1`
array2 = [2, 8, 22, 1] #Changed value of `array2`
print("New `array1` object ID: " + repr(id(array1)))
print("New `array2` object ID: " + repr(id(array2)))
print(list(g))
相关问题