Python在对象实例化时是复制值还是引用?

11 投票
2 回答
4109 浏览
提问于 2025-04-15 23:33

这是个简单的问题,也许,但我找不到合适的关键词在谷歌上搜索答案。我习惯在把对象传入对象构造函数时,先复制一份对象,像这样:

...
def __init__(self, name):
    self._name = name[:]
...

然而,当我运行下面的测试代码时,似乎并不需要这样,因为Python在创建对象时会自动深拷贝对象的值:

>>> class Candy(object):
...     def __init__(self, flavor):
...             self.flavor = flavor
...
>>> flav = "cherry"
>>> a = Candy(flav)
>>> a
<__main__.Candy object at 0x00CA4670>
>>> a.flavor
'cherry'
>>> flav += ' and grape'
>>> flav
'cherry and grape'
>>> a.flavor
'cherry'

那么,事情的真相是什么呢?谢谢!

补充:

感谢@Olivier给出的精彩回答。下面的代码展示了一个更好的例子,说明Python确实是通过引用来复制的:

>>> flav = ['a','b']
>>> a = Candy(flav)
>>> a.flavor
['a', 'b']
>>> flav[1] = 'c'
>>> flav
['a', 'c']
>>> a.flavor
['a', 'c']

2 个回答

1

看起来好像不是必须的

看起来?你的问题完全是关于设计和意义的。这不是一个偏好或习惯的问题。

这个类的约定是否包括可以修改一个可变的参数?如果是这样,就不要复制。

这个类的约定是否声明一个可变的参数不会被修改?如果是这样,你就必须复制。

你的问题完全可以通过这个类的约定定义来回答。

14

这是因为字符串是不可变的。

运算符+=有点让人困惑,实际上它会重新赋值给它所作用的变量,如果这个对象是不可变的:

s = 'a'
ids = id(s)
s += 'b'
ids == id(s) # False, because s was reassigned to a new object

所以在你的例子中,一开始,flava.flavor都指向同一个字符串对象:

flav --------\
               'cherry'
a.flavor ----/

但是当你写flav += 'and grape'时,变量flav会被重新赋值为一个新的字符串对象:

flav --------> 'cherry and grape'
a.flavor ----> 'cherry' # <-- that string object never changes

这让人感到困惑,因为通常情况下,当你对一个变量使用运算符时,它不会改变这个变量。但在不可变对象的情况下,它确实会重新赋值这个变量。

所以你问题的最终答案是,是的,在实例化时复制对象是有意义的,特别是当你期望得到一个可变对象时(这通常是情况)。如果对象是不可变的,复制它也不会有坏处。

撰写回答