构造函数处理可选参数时表现奇怪

16 投票
2 回答
12868 浏览
提问于 2025-04-15 23:06

可能重复的问题:
在Python中,最少惊讶原则:可变默认参数

我想了解一下Python中__init__构造函数的行为和影响。看起来当有一个可选参数时,如果你试图把一个已有的对象设置为一个新对象,已有对象的可选值会被保留并复制。

看一个例子:

在下面的代码中,我想创建一个树形结构,里面有节点和可能有很多子节点。在第一个类NodeBad中,构造函数有两个参数,一个是值,另一个是可能的子节点。而第二个类NodeGood只接收节点的值作为参数。两个类都有一个addchild方法,用来给节点添加子节点。

使用NodeGood类创建树时,一切都按预期工作。然而,当用NodeBad类做同样的事情时,似乎子节点只能添加一次!

下面的代码会产生以下输出:

Good Tree
1
2
3
[< 3 >]
Bad Tree
1
2
2
[< 2 >, < 3 >]

发生了什么?

这里是例子:

#!/usr/bin/python
class NodeBad:
  def __init__(self, value, c=[]):
    self.value = value
    self.children = c
  def addchild(self, node):
    self.children.append(node)
  def __str__(self):
    return '< %s >' % self.value
  def __repr__(self):
    return '< %s >' % self.value


class NodeGood:
  def __init__(self, value):
    self.value = value
    self.children = []
  def addchild(self, node):
    self.children.append(node)
  def __str__(self):
    return '< %s >' % self.value
  def __repr__(self):
    return '< %s >' % self.value

if __name__ == '__main__':
  print 'Good Tree'
  ng = NodeGood(1) # Root Node
  rootgood = ng
  ng.addchild(NodeGood(2)) # 1nd Child
  ng = ng.children[0]
  ng.addchild(NodeGood(3)) # 2nd Child

  print rootgood.value
  print rootgood.children[0].value
  print rootgood.children[0].children[0].value
  print rootgood.children[0].children

  print 'Bad Tree'
  nb = NodeBad(1) # Root Node
  rootbad = nb
  nb.addchild(NodeBad(2)) # 1st Child
  nb = nb.children[0]
  nb.addchild(NodeBad(3)) # 2nd Child

  print rootbad.value
  print rootbad.children[0].value
  print rootbad.children[0].children[0].value
  print rootbad.children[0].children

2 个回答

3

可变的默认参数常常让人感到困惑。

可以看看这个回答:“最小惊讶原则”和可变默认参数

17

问题在于,默认的可选参数值只有一个实例。所以举个例子,如果你写了 def __init__(self, value, c=[]):,那么每次调用这个方法时,都会传入同一个列表 []

所以,基本上你应该只使用不可变的数据类型,比如 None,作为可选参数的默认值。例如:

def __init__(self, value, c=None):

然后你可以在方法内部创建一个新的列表:

if c == None:
  c = []

撰写回答