Python循环内/外部的字典

3

我想更改字典中的一个元素,但如果我将new_alien放在循环外面,那么字典中的所有元素都会被更改。为什么会这样?

#ALL aliens get changed when I put put the dictionary up here
new_alien = {'colour': 'green'}

aliens=[]

for alien_number in range(3):
    #if i put new_alien here only one alien dictionary gets changed in the for loop (correct code)
    #new_alien = {'colour': 'green'}
    aliens.append(new_alien)

print(aliens)

for alien in aliens[0:1]:
    if (alien['colour'] == 'green'):
        alien['colour'] = 'yellow'

print(aliens[0:2])

如果new_alien在循环外部,则输出:

[{'colour': 'green'}, {'colour': 'green'}, {'colour': 'green'}]
[{'colour': 'yellow'}, {'colour': 'yellow'}]

如果new_alien在循环内,输出结果:

[{'colour': 'green'}, {'colour': 'green'}, {'colour': 'green'}]
[{'colour': 'yellow'}, {'colour': 'green'}]

请注意,在循环之外,所有外星词典:颜色都被改为黄色。如有疑问,请解释。谢谢!

1
因为您将同一个字典三次附加到列表中。 - juanpa.arrivillaga
6个回答

1
案例1:因此,当您将new_alien = {'colour': 'green', 'points': 5, 'speed': 'slow'}附加到列表中时,它引用内存中相同的对象。您可以通过id(Your_Object_Name)检查内存引用。基本上它像一个全局变量。 案例2:当您尝试从循环内部声明并将其附加到列表中时,它会创建具有不同内存引用的不同对象。

1
谢谢,我明白了!:) 感谢你的帮助! - SlowSloth

1

Python Tutor 是一个在线应用程序,它可以展示代码执行的过程。您可以将代码粘贴到其中,看到 Python 解释器如何运行代码。

以下是简单文本中的解释。

下面的语句创建了一个新的字典对象,而 new_alien 只是该字典对象的引用。

new_alien = {'colour': 'green', 'points': 5, 'speed': 'slow'}

如果你将此语句放在循环外,列表中的所有元素都会引用相同的字典对象(或相同的内存空间),因为你没有为每个元素创建新的对象。

抱歉,我仍然有困难理解。在第一个for循环之后,“aliens”有3个绿色的外星人。接下来的循环会将其中一个外星人变成黄色,但为什么会改变另外两个呢? - SlowSloth
1
不,你没有三个外星人,你只有三个指向同一个外星人的引用。 - laven_qa
太棒了!非常感谢您的帮助,Python Tutor非常有用! - SlowSloth
链接似乎已经失效 - 端点返回“禁止访问”。 - Zoe stands with Ukraine

1
如果你将new_alien放在外面,你会一遍又一遍地添加相同的对象。您可以使用id()打印出对象的标识来查看我在这里的意思:
循环内部的new_alien
for alien_number in range(3): 
    new_alien = {'colour': 'green', 'points': 5, 'speed': 'slow'}
    print(id(new_alien))
    aliens.append(new_alien)

它输出不同的对象标识:

1577811864456
1577811864528
1577811864960

因此,每次都会添加不同的对象。对于new_alien有不同的引用,3个单独的对象。如果更改一个对象,则只有一个对象在此处发生更改。这是正确的做法。
循环外的new_alien:
new_alien = {'colour': 'green', 'points': 5, 'speed': 'slow'}

for alien_number in range(3):
    print(id(new_alien))
    aliens.append(new_alien)

您获得相同的身份:
2763267730312
2763267730312
2763267730312

这意味着您一遍又一遍地附加相同的对象。这意味着如果您更改一个对象,则所有对象都将更改。这是因为它们都是指向同一对象的引用。如果您想在将来修改它们,那么这种方法行不通。

谢谢!我现在明白问题了。只是想澄清一下,当我三次附加相同的对象时,它仍将打印这些相同的3个对象3次。另一个问题是,为什么循环内外不同。我知道我错在哪里,只是想知道为什么。 - SlowSloth
@SlowSloth,这只是Python中的一件事情。如果你在循环外部声明一个对象一次,那就只有一个对象。这就是为什么当你打印它时,会打印出相同的对象3次。而如果你在循环中创建对象,每次迭代都会创建一个新的对象。除非你不想修改new_alien,否则在循环外部声明它是可以的。否则,你会遇到麻烦。 - RoadRunner
谢谢!我不知道这一点,因为我的输出看起来都一样。非常感谢。 - SlowSloth
没问题,请确保给任何有帮助的答案点赞,并在你发现它有用时选择一个最佳答案。 - RoadRunner

1
如果你将它放在外面,你就会向字典aliens添加相同的new_alien对象。如果你将它放在里面,你就会创建3个不同的new_alien对象,然后将它们附加到aliens中。
for loop (correct code)
    new_alien = {'colour': 'green'}<-create a new dictionary {"c": "g"} and put it 
                                     under the name new_alien
    aliens.append(new_alien) # add the new dictionary

尽管它们都添加了相同的变量new_alien,但在每次循环中绑定到new_alien的内容是不同的。每当你有一个赋值语句new_alien = {'colour': 'green'}时,你就会将new_alien重新绑定到一个新创建的字典上。因此,当你稍后更改aliens中的一个时,它不会影响其他的。
在另一个情况下:new_alien = {'colour': 'green'}位于循环外部,相同的对象引用被插入到字典中3次。当您将其中一个更改为"yellow"时,只会更改一个。(实际上只有一个可以更改,尽管看起来有3个。)但是,在打印时,您还打印了存储的相同字典3次!这就是为什么它给出相同结果的原因。

概括:

  • 一个对象可以有不同的名称,都指向同一个对象。
  • 变量名可以在不同的时刻指向不同的对象。

在第一种情况下,在每个步骤执行循环时,new_alien 指向不同的字典对象。

在后一种情况下,new_alien 在循环之外,我们可以看到 aliens[0]aliens[1]aliens[2] 都指向同一个字典对象。你改变其中一个,只有一个对象被改变。你改变其中两个,仍然只有一个字典被改变。你读取所有三个,实际上只是读取同一个字典三次。


0

这被称为浅拷贝,当您附加该项时,实际上附加的是同一对象,并且当您对其进行任何修改时,它都会影响主对象:

解决方案:

您应该使用深拷贝:

from copy import deepcopy
#ALL aliens get changed when I put put the dictionary up here
new_alien = {'colour': 'green'}

aliens=[]

for alien_number in range(3):
    #if i put new_alien here only one alien dictionary gets changed in the for loop (correct code)
    #new_alien = {'colour': 'green'}
    aliens.append(deepcopy(new_alien))


for alien in aliens[0:1]:
    if (alien['colour'] == 'green'):
        alien['colour'] = 'yellow'

print(aliens[0:2])
print(new_alien)

输出:

{'colour': 'green'}
[{'colour': 'yellow'}, {'colour': 'green'}]
{'colour': 'green'}

0
在Python中,对象在底层被视为内存中的地址。因此,对象的变量名只是对计算机内存中存储该对象的位置的引用。如果您运行此代码行:
new_alien = {'colour': 'green', 'points': 5, 'speed': 'slow'}

这将在内存中创建一个对象,可以通过new_alien变量引用。如果此行在循环之外,则仅运行一次,而如果在循环内部,则运行多次。当此行运行多次时,意味着在内存中创建了多个对象,但是当它仅运行一次时,只会在内存中创建一个对象,这就是为什么当它在外部声明时它们都被更新的原因;因为它们都在幕后引用相同的内存位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接