2012-08-09 80 views
0

可能重复:
“Least Astonishment” in Python: The Mutable Default Argument了解Python的继承和初始化

在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不同?非常感谢你的帮助!

回答

1

您的默认参数是[],这是Python常见的错误。 tutorial

重要警告:默认值只计算一次。当默认值是可变对象(如 列表,字典或大多数类的实例)时,此 会有所不同。

1

它与继承,类或实例变量无关。考虑下面的代码:

>>> def f(a=[]): 
...  a.append(1) 
...  print a 
... 
>>> f.func_defaults 
([],) 
>>> f() 
[1] 
>>> f() 
[1, 1] 
>>> f.func_defaults 
([1, 1],) 

函数参数的默认值只被计算一个并存储在函数对象中。每次调用f时,它都使用相同的列表进行操作。正如你的情况一样。

0

正如其他人所说的 - 这与使用空列表作为默认值有关,而不是类继承行为。

做正确的方法是:

class Base(object): 
    # Variant 1 
    def __init__(self, records=None): 
     if records is None: 
      records = [] 
     self._records = records 

因此确保一个新的列表实例每一个基类实例化时创建。将它放入代码中的方式是,在用Python解析类体时实例化一个列表对象 - 每次运行__init__方法时,将使用list的一个实例作为默认参数。由于对象拥有引用并更改同一列表,所以更改在共享Base类的所有其他对象中都可见。