1

我使用生成器生成一些列表。但是,它不像我预期的那样工作。我首先使用 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 的结果相似。

4

1 回答 1

3

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

当您 yield 时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]]

numgen1你可以通过 yield来“修复” ,list(curr)因为list(curr)返回一个与 in 相同元素的新列表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是一个列表,它改变索引 2 处curr[2] += delta内容yield curr,但产生完全相同的列表。

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


这是就地修改列表意味着什么的另一种观点:如果列表的内容发生更改,则修改是就地完成的,而列表本身的内存地址不会改变。

id(obj)返回对象的内存地址obj。请注意,修改curr[2]不会更改idof curr

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只是被重定向以引用新内存地址处的新值。

于 2013-11-09T14:05:58.270 回答