我找到了这个示例,但我不明白为什么它无法正常工作?
我以为它必须输出[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]
答案 0 :(得分:17)
原因是,在创建时,生成器(a for b in c if d)
仅评估c
(有时有时使b
可预测)。但是a
,b
,d
是在消耗时间(每次迭代)进行评估的。此处,在评估array
(d
)时,它使用了来自封闭范围的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)
仅评估生成器表达式中的第一个array
。 x
和array.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)
它将创建一个生成器,该生成器将遍历数组并搜索已经存在的值的计数是否大于零。因此,生成器仅查找1
,8
和15
,并且当您将值更改为另一个值时,生成器将再次查找先前的值,而不是新的值。因为它(生成器)在数组拥有它们时创建。
因此,如果将数千个值放在数组中,则仅查找这三个值。
答案 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))