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




您可以通过产生numgen1来“修复” list(curr),因为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会产生完全相同的列表。

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



这是就地修改列表的含义的另一种观点:如果列表的内容更改,而列表本身的内存地址没有更改,则修改就地完成。

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

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重定向为在新的内存地址处引用新值。

关于python - 生成器列表理解,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19876823/

10-13 03:43