![](/img/trans.png)
[英]Need help fixing a random sentence generator that uses for loops and list's
[英]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
可预测)。 但是a
, b
, d
在消耗时间(每次迭代)进行评估。 这里,它在评估d
( array.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)
时才会计算x
和array.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)
它将创建一个查看数组的生成器,并将搜索已存在的值的计数是否大于零。 所以你只发电机查找1
, 8
和15
,当你改变这些值到另一个,发电机只是看起来再次之前的值不是新的。 因为它(生成器)在数组有它们时创建。
所以如果你在数组中放入数千个值,它只会查找这三个值。
混淆,答案也是如此,在于: 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.