Python基类共享属性?

时间:2010-05-21 04:06:03

标签: python

test.py中的代码:

class Base(object):
    def __init__(self, l=[]):
        self.l = l

    def add(self, num):
        self.l.append(num)

    def remove(self, num):
        self.l.remove(num)

class Derived(Base):
    def __init__(self, l=[]):
        super(Derived, self).__init__(l)

Python shell会话:

Python 2.6.5 (r265:79063, Apr  1 2010, 05:22:20) 
[GCC 4.4.3 20100316 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> a = test.Derived()
>>> b = test.Derived()
>>> a.l
[]
>>> b.l
[]
>>> a.add(1)
>>> a.l
[1]
>>> b.l
[1]
>>> c = test.Derived()
>>> c.l
[1]

我期待“类似C ++”的行为,其中每个派生对象都包含自己的基类实例。这仍然是这样吗?为什么每个对象看起来共享相同的列表实例?

2 个答案:

答案 0 :(得分:17)

你犯了一个常见的Python新手错误。

请在此处查看我的回答: How should I declare default values for instance variables in Python?

简要解释一下,Python只解释类别定义一次。这意味着__init__()方法中声明的所有内容仅创建一次。或者,换句话说,您的[]列表默认参数只进行一次。

然后self.l = l每次创建新类时都会为同一个实例分配引用,因此就是您不期望的行为。

Pythonic方式是这个(部分代码):

def __init__(self, arg=None):
    if arg is None:
        arg = []
    self.arg = arg

此外,您应该考虑使用比l更好的命名约定,这很难理解,可能会被误认为是1|

答案 1 :(得分:2)

这称为可变的默认参数错误,通常由Python新手提出。当您将mutable作为默认参数时,当需要使用默认参数时,将跨实例使用相同的对象。为了更好地理解,请查看http://docs.python.org/tutorial/controlflow.html#default-argument-values

中的重要警告部分

在你的代码中,实例a在它的init调用中使用了可变的默认参数(一个空的列表对象),当你创建了b的实例时,它又调用了Base的init方法,再次使用了与a相同的对象。用在它的init中。在更简单的单词上,a.l和b.l指向相同的列表对象。

非常类似的讨论 - "Least Astonishment" and the Mutable Default Argument