Python 是否会在赋值时复制对象?

Does Python make a copy of objects on assignment?

提问人:Marcus Whybrow 提问时间:3/13/2010 最后编辑:Karl KnechtelMarcus Whybrow 更新时间:9/3/2022 访问量:64685

问:

当我尝试此代码时:

dict_a = dict_b = dict_c = {}
dict_c['hello'] = 'goodbye'

print(dict_a)
print(dict_b)
print(dict_c)

我预计它只会初始化 和 字典,然后在 中分配一个键,从而导致dict_adict_bdict_cdict_c

{}
{}
{'hello': 'goodbye'}

但它似乎反而具有复制效果:

{'hello': 'goodbye'}
{'hello': 'goodbye'}
{'hello': 'goodbye'}

为什么?

评论

0赞 Marcus Whybrow 3/15/2010
没有教程,我只是应用了我认为在 Java 等语言中有效的概念。
0赞 Karl Knechtel 4/3/2022
Смотритетакже: stackoverflow.com/questions/17246693/...
2赞 Karl Knechtel 8/12/2022
“但是,通过效果进行复制是无缝的:”这里的行为正是因为它没有复制。复制意味着每个变量都有自己单独的值。相反,它们得到的是相同的空字典,只是名称不同。{}
2赞 chepner 9/3/2022
在提出这个问题时,它并不存在,但现在发现这个问题的人应该阅读 nedbatchelder.com/text/names.html

答:

60赞 danben 3/13/2010 #1

这是因为在 Python 中,变量(名称)只是对单个对象的引用。当您分配 时,您实际上是在将内存地址(或指针,如果您愿意的话)从 复制到 。该字典仍然有一个实例。dict_a = dict_bdict_bdict_a

若要获得所需的行为,请使用该方法,或者使用 if 您的字典可能具有嵌套的字典或其他嵌套对象。dict.copycopy.deepcopy

>>> a = {1:2}
>>> b = a.copy()
>>> b
{1: 2}
>>> b[3] = 4
>>> a
{1: 2}
>>> b
{1: 2, 3: 4}
>>> 

评论

1赞 Marcus Whybrow 3/13/2010
所以字典是对象,比如说整数变量是否仍然如此,或者变量的类型对它作为对象没有影响吗?
1赞 danben 3/13/2010
整数是不可变的,所以我不确定你打算如何重现这种行为。在任何情况下,某些类型的文本值都会被内禁(即每个类型只有一个实例)。您可以通过函数验证包含相同值的两个整数变量是否实际指向整数的同一实例。id()
2赞 danben 3/13/2010
无论如何,Python 中的一切都是一个对象。要查看属于整数对象 1 的函数,可以执行 .dir(1)
18赞 jcdyer 3/14/2010
一切都是一个对象,变量是我们分配给对象的名称(而不是相反!因此,如果你这样做了,那么一切都被分配给对象,然后被分配给对象。对于列表、字典和集合等可变对象,您可以更改对象本身,分配给该对象的所有名称都可以看到,但对于整数、字符串、元组和其他不可变对象,您所能做的就是将名称分配给不同的对象。a = b = c = 1; a +=11a2
2赞 Mike Graham 3/14/2010
“对象只是引用”事实并非如此。对象就是对象。名称引用它们。
1赞 msalib 3/13/2010 #2

您的第一个赋值将相同的字典对象分配给变量 dict_a、dict_b 和 dict_c。它等价于 dict_c = {};dict_b = dict_c;dict_a = dict_c。

1赞 mdeous 3/14/2010 #3

正如 danben 之前所说,你只是将相同的字典复制到 3 个变量中,以便每个变量都引用同一个对象。

为了获得你想要的行为,你应该在每个变量中实例化一个不同的字典:

>>> dict_a, dict_b, dict_c = {}, {}, {}
>>> dict_c['hello'] = 'goodbye'
>>> print dict_a
{}
>>> print dict_b
{}
>>> print dict_c
{'hello': 'goodbye'}
>>>

评论

0赞 Marcus Whybrow 3/14/2010
谢谢,当我偶然发现这个主题时,这就是我打算做的。虽然它不是问题的答案,但非常感谢。
10赞 Jeffrey Jose 3/15/2010 #4

即使

>>> dict_a, dict_b, dict_c = {}, {}, {}

在大多数情况下是正确的方法,当它超过 3 时,它看起来很奇怪

想象

>>> a, b, c, d, e, f = {}, {}, {}, {}, {}, {}

如果我想初始化 3 个以上的东西,我使用

>>> a, b, c, d, e, f, = [dict() for x in range(6)]

评论

4赞 SingleNegationElimination 7/23/2011
“注意:不要对 range(6)] 中的 x 使用 [{}”为什么不呢?元素表达式在每次迭代中执行一次,每次执行文本时,它都会返回一个新的字典。事实上,这应该是首选,因为它避免了函数调用。{}dict()
0赞 Jeffrey Jose 7/24/2011
@TokenMacGuy,是和不是。现在我重新阅读了我写的内容,我明白了你的意思。 每次都初始化。也就是说,这只是 的语法糖。使用前者不会保存任何内容。{}{}dict()
6赞 SingleNegationElimination 7/25/2011
Jeffrey Jose:怀疑托马斯是吗?import dis; print dis.dis(lambda: dict()); print dis.dis(lambda:{})
1赞 Jeffrey Jose 7/25/2011
@TokenMacGuy。哎呀!在提出任何索赔之前,我应该这样做。我纠正了。dis
0赞 chepner 9/3/2022
拥有大量名称相似的变量通常不是首先要走的路。.dicts = {x: {} for x in ["a", "b", "c"]}
0赞 Domagoj Vidović 6/14/2020 #5

我同意上面所说的。这里的关键是,在 Python 中,赋值表示对对象的引用。 我试图自己掌握这个概念,我认为了解在哪种情况下创建新对象以及何时更改现有对象是否重要。

在上面的示例中,行:

dict_c['hello'] = 'goodbye'

不会创建新对象。它仅更改 dict_a、dict_b 和 dict_c 引用的对象。

相反,如果你写了:

dict_c = {'hello': 'goodbye'}

它将创建一个新对象,该对象将由dict_c引用。Dict_a 和 dict_b 仍然指向空对象。

在这种情况下,如果运行:

print dict_a
print dict_b
print dict_c

你会得到:

{}
{}
{'hello': 'goodbye'}