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 ++”的行为,其中每个派生对象都包含自己的基类实例。这仍然是这样吗?为什么每个对象看起来共享相同的列表实例?
答案 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