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命令行会话:
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 个回答
2
这被称为可变默认参数的错误,很多刚接触Python的人都会犯这个错误。当你把一个可变对象(比如列表)作为默认参数时,如果这个默认参数被多次使用,实际上会用到同一个对象。想要更深入了解,可以查看重要警告部分。
在你的代码中,实例a在初始化时使用了一个可变的默认参数(一个空列表),而当你创建实例b时,它又调用了Base的初始化方法,这时候又用了a在初始化时使用的那个同样的对象。简单来说,a.l和b.l指向的是同一个列表对象。
还有一个非常相似的讨论 - “最小惊讶原则”和可变默认参数
20
你犯了一个常见的Python新手错误。
你可以看看我在这里的回答: 我应该如何为Python中的实例变量声明默认值?
简单来说,Python只会一次解释类的定义。这意味着在__init__()
方法中声明的所有内容只会创建一次。换句话说,你的[]
列表默认参数只会被创建一次。
然后self.l = l
每次你创建一个新类时,都会指向同一个实例,这就是你没有预料到的行为。
Python推荐的做法是这样的(部分代码):
def __init__(self, arg=None):
if arg is None:
arg = []
self.arg = arg
另外,你应该考虑使用比l
更好的命名方式,因为l
很难阅读,可能会被误认为是1
或|
。