可能重复:
“Least Astonishment” in Python: The Mutable Default Argument
在Python 2.7中,请考虑我有以下代码:
class Base(object):
# Variant 1
def __init__(self, records=[]):
self._records = records
# Variant 2
# def __init__(self, records=[]):
# self._records = []
# if records:
# self._records = records
def append(self, value):
self._records.append(value)
class ChildA(Base):
pass
class ChildB(Base):
pass
a = ChildA()
b = ChildB()
a.append(100)
b.append(200)
print a._records
print b._records
如果我使用变量1来初始化我的基类,则self._records的行为类似于类变量。使用变量1执行代码来初始化我的基类,我得到了输出:
[100, 200]
[100, 200]
使用变量2初始化我的基类,self._records的行为类似于实例变量(如预期的那样)。使用变量2执行代码来初始化我的基类,我得到输出:
[100]
[200]
这两种变体有什么区别?为什么变体1与变体2的工作方式不同?非常感谢你的帮助!
答案 0 :(得分:1)
您的默认参数是[]
,这是Python常见的陷阱。请参阅tutorial:
重要警告:默认值仅评估一次。这个 当默认值是可变对象(如a)时,会有所不同 列表,字典或大多数类的实例。
答案 1 :(得分:1)
它与继承,类或实例变量无关。考虑下一个代码:
>>> def f(a=[]):
... a.append(1)
... print a
...
>>> f.func_defaults
([],)
>>> f()
[1]
>>> f()
[1, 1]
>>> f.func_defaults
([1, 1],)
函数参数的默认值仅计算一个并存储在函数对象中。每次调用f
时,它都使用相同的列表进行操作。就像你的情况一样。
答案 2 :(得分:0)
正如其他人所说的那样 - 这与使用空列表作为默认值有关,而不是使用类继承行为。
正确的做法是:
class Base(object):
# Variant 1
def __init__(self, records=None):
if records is None:
records = []
self._records = records
因此,每次实例化Base类时,都要确保创建新的列表实例。你把它放在你的代码中的方式,当你的类体被Python解析时,实例化一个列表对象 - 每次list
方法运行时,__init__
的一个实例被用作默认参数。由于对象包含引用并更改相同的列表,因此更改在共享Base
类的所有其他对象中可见。