为什么Python中对象之间共享默认值?
在Python中,我们经常会遇到这样一种情况:创建一个类的对象时,这些对象会共享该类默认值属性的值。我们首先来看下面这段代码:
class MyClass:
my_list = []
obj1 = MyClass()
obj2 = MyClass()
obj1.my_list.append(1)
print(obj1.my_list) # [1]
print(obj2.my_list) # [1]
在这段代码中,我们定义了一个类MyClass
,并定义了一个默认值为空列表的类属性my_list
。接着我们创建了两个类对象obj1
和obj2
,并分别向obj1
实例中添加了一个值为1的元素。最后打印出了这两个对象的my_list
属性值;
我们会发现,obj1.my_list
和obj2.my_list
的值都为[1]
。这是为什么呢?
阅读更多:Python 教程
Python中的共享默认值
Python 中的类属性在不同的实例之间是共享的。如果属性的值是可变对象(如列表,字典等),那么任何对该对象的更改都将影响到所有类对象的实例。这包括不仅包括在类构造函数中设置的默认值,还包括其他的类方法实现的属性更改。
相信许多人初次接触这个场景时都会感到非常诧异,但实际上这是符合 Python 运行机制的。
问题出现在类属性的赋值。当我们赋值给一个类属性时,Python 解释引擎会将该值存储在某个单个内存地址中,并将该地址分配给该类属性。当类属性被访问时,实例中不存在该属性,因此,Python 解释器将寻找类上的属性。
因此,当我们使用my_list
作为默认值时,它为所有实例共享。
此外,在上述代码执行中,我们在obj1
实例中添加了一个元素1
。由于该属性是可变的,因此更改将影响到默认值及所有实例。因此,obj1.my_list
和obj2.my_list
均为[1]
。
如何避免类属性的共享
由于共享默认值是 Python 语言特性,因此我们不能完全避免这种情况。然而,我们可以通过以下方法来减少此行为的影响:
- 使用不可变对象作为属性默认值;
- 在构造函数中手动定义默认值;
- 在使用类变量或属性时明确指定它的使用者。
以下代码展示了如何使用元组而不是列表来避免我们实例对象之间的共享默认行为:
class MyClass:
my_list = ()
obj1 = MyClass()
obj2 = MyClass()
try:
obj1.my_list.append(1)
except AttributeError:
print("Error! Cannot modify immutable objects.")
print(obj1.my_list) # ()
在这段代码中,我们仍定义了一个默认值,但将其更改为元组中的空值。因为元组是不可变对象,所以无法修改元组中的任何值,从而避免共享属性值的问题。然后,在尝试向obj1.my_list
中添加一个元素时,我们捕获了一个属性错误,因为元组不支持使用append()添加元素。
结论
Python 中共享默认值是 Python 解释器的一种自然行为。虽然这种行为可能会产生一些问题,但开发人员可以通过使用不可变对象作为默认值、在构造函数中手动定义默认值以及在使用类变量或属性时明确指定其使用者来减少这种行为的影响。