繁体   English   中英

列表理解与发电机

[英]list comprehension with generator

我用generator来生成一些列表。 但是,它不像我预期的那样工作。 我首先使用numgen1生成列表,但它无法正常工作。 然后我切换到numgen2,这可以给我正确的想法。 但是numgen1和numgen2基本相同(至少我认为),为什么它们表现得如此不同? 愿任何人给我一些解释吗?

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

这是输出。

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

跟进问题:

在阅读unutbu的答案之后,我又写了一个用于测试unutbu所说的生成器。 但是发电机不像unutbu那样表现。 我对生成器是否产生指针或值的副本感到困惑。

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

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

这是输出。 [1,2,3,4,5,6,7,8,9]

这次我尝试生成一些数字而不是一些列表。 但为什么不是列表9中的所有元素? 我没有创建一个新的数字,我只是产生相同的数字(curr)。 我期望numgen3的结果应该类似于numgen1的结果。

curr是一个清单。 curr[2] += delta正在就地修改列表。

当你产生curr ,你会一遍又一遍地产生相同的列表。 当您打印Output1您会看到同一列表被多次打印。

当您产生[curr[0], curr[1], curr[2]]您将生成一个新列表。 因此,当您打印Output2 ,您会看到不同的值。


注意修改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]]

您可以通过产生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)]

产量

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

关于numgen3

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

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

它有助于在可变值和不可变值之间进行精神区分。 列表是可变的,诸如ints类的数字是不可变的。

列表是容器。 您可以在不更改对容器的引用的情况下改变其内容。 因此curr是一个列表, curr[2] += delta改变索引2处的内容 ,但yield curr产生相同的列表。

numgen3curr是一个不可变的int curr += deltacurr分配给新的immutable int 它不再引用同一个对象。 yield curr产生该值。 这些不同的值在列表推导中累积,因此您可以看到包含不同值的结果。


下面是什么意思修改就地列表另一个角度来看:修改在原地做,如果列表的内容发生变化,而列表中的内存地址本身并没有改变。

id(obj)返回对象obj的内存地址。 请注意,修改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

将其与增加分配给int的变量时发生的情况进行比较:

In [191]: curr = 1

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

In [193]: curr += 1

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

这里, curr没有就地修改。 curr被简单地重定向以在新的内存地址处引用新值。

暂无
暂无

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

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