0

可能的重复:
Python 中的“Least Astonishment”:可变默认参数

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

4

3 回答 3

1

它与继承、类或实例变量无关。考虑下一个代码:

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

函数参数的默认值仅被评估并存储在函数对象中。每次f调用它都使用相同的列表。就像你的情况一样。

于 2012-08-09T09:09:06.637 回答
1

您的默认参数是[],这是 Python 的常见缺陷。在教程中查看更多信息:

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

于 2012-08-09T08:58:51.307 回答
0

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

正确的做法是:

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

因此,确保每次实例化 Base 类时都会创建一个新的列表实例。你把它放在你的代码中的方式是,当你的类主体被 Python 解析时,一个列表对象被实例化——并且每次运行该方法时,一个实例list被用作默认参数。__init__由于对象持有引用并更改相同的列表,因此更改在共享Base该类的所有其他对象中可见。

于 2012-08-09T11:53:02.327 回答