简体   繁体   English

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

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

I found this example and I can't understand why it works unpredictably?我找到了这个例子,但我不明白为什么它会不可预测地工作? I supposed it must output [1, 8, 15] or [2, 8, 22] .我想它必须是 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]

The reason is that, at creation time, the generator (a for b in c if d) only evaluates c (which sometimes makes b predictable as well). 原因是,在创建时,生成器(a for b in c if d)仅评估c有时也使b可预测)。 But a , b , d are evaluated at consumption time (at each iteration). 但是abd在消耗时间(每次迭代)进行评估。 Here, it uses the current binding of array from the enclosing scope when evaluating d ( array.count(x) > 0 ). 这里,它在评估darray.count(x) > 0 )时使用封闭范围内的array当前绑定。

You can for instance do: 你可以做:

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

Without having declared a in advance. 无需宣布a提前。 But, you have to make sure a exists when the generator is consumed. 但是,你必须确保a当发电机消耗存在。

But you cannot do similarly: 但你不能做同样的事情:

g = (x for x in a if True)

Upon request: 根据要求:

You can observe similar (however not identical) patterns with a common generator function: 您可以使用常见的生成器函数观察相似(但不相同)的模式:

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]

The generator function does not execute any of its body ahead of consumption. 生成器函数在消耗之前不执行任何正文。 Hence, even the array in the for-loop header is bound late. 因此,即使for-loop头中的array也是迟到的。 An even more disturbing example occurs where we "switch out" array during iteration: 一个更令人不安的例子发生在我们在迭代期间“切换” 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

From the docs on Generator expressions : Generator表达式的文档:

Variables used in the generator expression are evaluated lazily when the __next__() method is called for the generator object (in the same fashion as normal generators). 当为生成器对象调用__next__()方法时,与生成器表达式中使用的变量会被懒惰地评估 (与普通生成器一样)。 However, the iterable expression in the leftmost for clause is immediately evaluated , so that an error produced by it will be emitted at the point where the generator expression is defined, rather than at the point where the first value is retrieved. 但是,会立即计算最左侧for子句中的可迭代表达式,以便在定义生成器表达式的位置(而不是在检索第一个值的位置)发出由它生成的错误。

So when you run 所以当你跑步的时候

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

only the first array in the generator expression is evaluated. 只计算生成器表达式中的第一个array x and array.count(x) will only be evaluated when you call next(g) . 只有在调用next(g)时才会计算xarray.count(x) next(g) Since you make array point to another list [2, 8, 22] before consuming the generator you get the 'unexpected' result. 由于使用生成器之前使array指向另一个列表[2, 8, 22] 因此会得到“意外”结果。

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

when you first create the array and assign the elements in it, elements of the array points to some memory location and generator keeps that location (not the array's) for its execution. 当您第一次创建数组并在其中分配元素时,数组的元素指向某个内存位置,并且生成器会保留该位置(而不是数组)以执行它。

but when you modify its elements of the array it gets changed but as '8' is common for both of them python does not reassign it and points to the same element after modification. 但是当你修改它的数组元素时它会被改变,但是因为'8'对于它们都是常见的,python不会重新分配它并在修改后指向相同的元素。

Look the below example for better understanding 请查看以下示例以便更好地理解

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

Output 产量

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

Actually, it is not really crazy if you look more carefully. 实际上,如果你仔细观察,它并不是真的很疯狂。 look at 看着

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

it will create a generator that looks through the array and will search if the count of already existing values is more than zero. 它将创建一个查看数组的生成器,并将搜索已存在的值的计数是否大于零。 so your generator only looks for 1 , 8 and 15 , and when you change the values to another, the generator just looks for the previous values again not new ones. 所以你只发电机查找1815 ,当你改变这些值到另一个,发电机只是看起来再次之前的值不是新的。 because it(generator) creates when array had them. 因为它(生成器)在数组有它们时创建。

so if you put thousands of values in the array it only looks for those three only. 所以如果你在数组中放入数千个值,它只会查找这三个值。

The confusion, and so is the answer, lies in the line: g = (x for x in array if array.count(x) > 0) 混淆,答案也是如此,在于: g = (x for x in array if array.count(x) > 0)
If we simplify this line then it will become: g = (x for x in array1 if array2.count(x) > 0) 如果我们简化这一行,那么它将成为: g = (x for x in array1 if array2.count(x) > 0)

Now, when generator is created then it keeps the reference of array1 object. 现在,当创建生成器时,它保留了array1对象的引用。 So even if I will change the value of array1 to any other value (ie set it to a new array object), it will not affect the generator 's copy of array1 . 因此,即使我将array1的值更改为任何其他值(即将其设置为新的数组对象),它也不会影响生成器array1副本。 Because only array1 is changing it's object reference. 因为只有array1正在改变它的对象引用。 But array2 is checked dynamically. 但是动态检查array2 So if we change its value it will be reflected. 因此,如果我们改变其价值,它将被反映出来。

You can see outputy from following code to understand it batter. 您可以从以下代码中看到outputy以了解它的结果。 See it working online here : 看到它在这里工作

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

Output: 输出:

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