繁体   English   中英

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

[英]Generator expression uses list assigned after the generator's creation

我找到了这个例子,但我不明白为什么它会不可预测地工作? 我想它必须是 output [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]

原因是,在创建时,生成器(a for b in c if d)仅评估c有时也使b可预测)。 但是abd在消耗时间(每次迭代)进行评估。 这里,它在评估darray.count(x) > 0 )时使用封闭范围内的array当前绑定。

你可以做:

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-loop头中的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

Generator表达式的文档:

当为生成器对象调用__next__()方法时,与生成器表达式中使用的变量会被懒惰地评估 (与普通生成器一样)。 但是,会立即计算最左侧for子句中的可迭代表达式,以便在定义生成器表达式的位置(而不是在检索第一个值的位置)发出由它生成的错误。

所以当你跑步的时候

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

只计算生成器表达式中的第一个array 只有在调用next(g)时才会计算xarray.count(x) next(g) 由于使用生成器之前使array指向另一个列表[2, 8, 22] 因此会得到“意外”结果。

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

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

但是当你修改它的数组元素时它会被改变,但是因为'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]

实际上,如果你仔细观察,它并不是真的很疯狂。 看着

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

它将创建一个查看数组的生成器,并将搜索已存在的值的计数是否大于零。 所以你只发电机查找1815 ,当你改变这些值到另一个,发电机只是看起来再次之前的值不是新的。 因为它(生成器)在数组有它们时创建。

所以如果你在数组中放入数千个值,它只会查找这三个值。

混淆,答案也是如此,在于: g = (x for x in array if array.count(x) > 0)
如果我们简化这一行,那么它将成为: g = (x for x in array1 if array2.count(x) > 0)

现在,当创建生成器时,它保留了array1对象的引用。 因此,即使我将array1的值更改为任何其他值(即将其设置为新的数组对象),它也不会影响生成器array1副本。 因为只有array1正在改变它的对象引用。 但是动态检查array2 因此,如果我们改变其价值,它将被反映出来。

您可以从以下代码中看到outputy以了解它的结果。 看到它在这里工作

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))

输出:

Old `array1` object ID: 47770072262024
Old `array2` object ID: 47770072263816
New `array1` object ID: 47770072263944
New `array2` object ID: 47770072264008
[1, 8]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM