Python:新类实例中未创建变量的新实例

2 投票
2 回答
2084 浏览
提问于 2025-04-17 18:42

我发现了Python的一些奇怪行为(或者说我可能不太理解继承和属性默认值是怎么工作的)。

对于下面这段代码:

class A(object):
    def __init__(self, s):
        self.s = s
        print "in init", self.s

class B(A):
    def __init__(self, s = set()):
        super(B, self).__init__(s)
        print "after super", self.s
        self.s.add('foo')
        print '--------------'

if __name__ == "__main__":
    a = B()
    b = B()

我得到了以下输出:

in init set([])
after super set([])
--------------
in init set(['foo']) # Why it has value set in other object?!
after super set(['foo'])
--------------

当然,我希望第二个对象(b)里的self.s能用一个空集合来初始化,但不知道为什么它却从前一个对象那里获取了状态。这是为什么呢?我该怎么才能得到我想要的结果呢?

谢谢!

2 个回答

1

当你为一个参数设置默认值时,这个值是在你定义函数的时候计算出来的,而不是在你调用这个函数的时候。所以,它使用的是在你定义类的时候创建的那个set(),并且不断往里面添加更多的项目。

通常在Python中,我们的做法是把默认值设置为None,然后在__init__函数里明确地初始化一个新的set()(或者列表、字典,或者你想要的任何对象)。

class B(A):
  def __init__(self, s=None):
    if s is None:
      s = set()
9

你遇到了可变默认参数的陷阱。你在类 B 的 __init__ 方法中指定的 set() 作为默认值,其实是同一个对象,这个对象会被传递给你创建的每一个新的 B 实例。把它放到 self 中并不会创建一个新的副本,而只是创建了对同一个对象的引用。

你可能想要在每个新对象中创建一个副本,所以需要明确地这样做:

class A(object):
    def __init__(self, s):
        self.s = copy.copy(s)

撰写回答