简体   繁体   English

列表理解与发电机

[英]list comprehension with generator

I used generator to generate some lists. 我用generator来生成一些列表。 However, it doesn't work like what I expected. 但是,它不像我预期的那样工作。 I first used numgen1 to to generate the list while it doesn't work properly. 我首先使用numgen1生成列表,但它无法正常工作。 And I switched to numgen2 which can gave me what I want properly. 然后我切换到numgen2,这可以给我正确的想法。 But both the numgen1 and numgen2 are basically the same (at least I think), why do they behave so differently? 但是numgen1和numgen2基本相同(至少我认为),为什么它们表现得如此不同? May anyone give me some explanations? 愿任何人给我一些解释吗?

def numgen1(start, end, delta):
    curr=start 
    while curr[1] < end[1] or curr[2]<end[2]:
        yield curr
        curr[2] += delta


print 'Output1: ', [ i for i in numgen1([1,1,1],[1,1,5],1)]

def numgen2(start, end, delta):
    curr=start 
    while curr[1] < end[1] or curr[2]<end[2]:
        yield [curr[0], curr[1], curr[2]]
        curr[2] += delta


print 'Output2: ', [ i for i in numgen2([1,1,1],[1,1,5],1)]

Here are the outputs. 这是输出。

Output1:  [[1, 1, 5], [1, 1, 5], [1, 1, 5], [1, 1, 5]]
Output2:  [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]

Follow up question: 跟进问题:

After reading the answer from unutbu, I write one more generator that is used to test what unutbu said. 在阅读unutbu的答案之后,我又写了一个用于测试unutbu所说的生成器。 But the generator doesn't behave as unutbu said. 但是发电机不像unutbu那样表现。 I am so confused about whether a generator is yielding a pointer or a copy of the value. 我对生成器是否产生指针或值的副本感到困惑。

def numgen3(start, end, delta):
    curr=start 
    while curr<end:
        yield curr
        curr += delta

print list(numgen3(1,10,1))

Here is the output. 这是输出。 [1, 2, 3, 4, 5, 6, 7, 8, 9] [1,2,3,4,5,6,7,8,9]

This time I try to generate some numbers instead of some lists. 这次我尝试生成一些数字而不是一些列表。 But why aren't all the elements in the list 9? 但为什么不是列表9中的所有元素? I didn't create a new number, I just yield the same number (curr). 我没有创建一个新的数字,我只是产生相同的数字(curr)。 I expect the result of numgen3 should be similar to that of numgen1. 我期望numgen3的结果应该类似于numgen1的结果。

curr is a list. curr是一个清单。 curr[2] += delta is modifying the list in-place . curr[2] += delta正在就地修改列表。

When you yield curr , you are yielding the same list over and over. 当你产生curr ,你会一遍又一遍地产生相同的列表。 When you print Output1 you are seeing this same list being printed many times. 当您打印Output1您会看到同一列表被多次打印。

When you yield [curr[0], curr[1], curr[2]] you are generating a new list. 当您产生[curr[0], curr[1], curr[2]]您将生成一个新列表。 Thus, when you print Output2 , you see different values. 因此,当您打印Output2 ,您会看到不同的值。


Notice how modifying curr affects all items in result , because result is a list containing 3 items, each the same list curr : 注意修改curr如何影响result 所有项目 ,因为result是一个包含3个项目的列表,每个项目都是相同的列表curr

curr = [0,0,0]
result = [curr for i in range(3)]
print(result)
# [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

curr[2] = 100
print(result)
# [[0, 0, 100], [0, 0, 100], [0, 0, 100]]

You could "fix" numgen1 by yielding list(curr) since list(curr) returns a new list with the same elements as in curr (ie a "shallow copy"): 您可以通过产生list(curr)来“修复” numgen1 ,因为list(curr)返回一个新的列表,其中包含与curr相同的元素(即“浅拷贝”):

def numgen1(start, end, delta):
    curr=start 
    while curr[1] < end[1] or curr[2]<end[2]:
        yield list(curr)
        curr[2] += delta

print 'Output1: ', [ i for i in numgen1([1,1,1],[1,1,5],1)]

yields 产量

Output1:  [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]

Regarding numgen3 : 关于numgen3

def numgen3(start, end, delta):
    curr=start 
    while curr<end:
        yield curr
        curr += delta

print list(numgen3(1,10,1))

It helps to make a mental distinction between mutable and immutable values. 它有助于在可变值和不可变值之间进行精神区分。 A list is mutable, numbers such as ints are immutable. 列表是可变的,诸如ints类的数字是不可变的。

Lists are containers. 列表是容器。 You can mutate its contents without changing the reference to the container. 您可以在不更改对容器的引用的情况下改变其内容。 Thus curr is a list, curr[2] += delta mutates the contents at index 2, but yield curr yields the very same list. 因此curr是一个列表, curr[2] += delta改变索引2处的内容 ,但yield curr产生相同的列表。

In numgen3 , curr is an immutable int . numgen3curr是一个不可变的int curr += delta assigns curr to a new immutable int . curr += deltacurr分配给新的immutable int It no longer references the same object. 它不再引用同一个对象。 yield curr yields that value. yield curr产生该值。 Those different values are accumulated in the list comprehension, and thus you see the result containing different values. 这些不同的值在列表推导中累积,因此您可以看到包含不同值的结果。


Here is another perspective on what it means to modify a list in-place : The modification is done in-place if the contents of the list change, while the memory address of the list itself does not change. 下面是什么意思修改就地列表另一个角度来看:修改在原地做,如果列表的内容发生变化,而列表中的内存地址本身并没有改变。

id(obj) returns the memory address of the object obj . id(obj)返回对象obj的内存地址。 Notice that modifying curr[2] does not change the id of curr : 请注意,修改curr[2]不会更改currid

In [162]: curr = [0,0,0]

In [163]: id(curr)
Out[163]: 196192940

In [164]: curr[2] += 1

In [165]: curr
Out[165]: [0, 0, 1]

In [166]: id(curr)
Out[166]: 196192940

Compare that with what happens when you increment a variable assigned to an int : 将其与增加分配给int的变量时发生的情况进行比较:

In [191]: curr = 1

In [192]: id(curr)
Out[192]: 150597808

In [193]: curr += 1

In [194]: id(curr)
Out[194]: 150597796

Here, curr is not being modified in-place. 这里, curr没有就地修改。 curr is simply being redirected to reference a new value at a new memory address. curr被简单地重定向以在新的内存地址处引用新值。

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

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