python构造函数使用可选参数做一些奇怪的事情

Python constructor does weird things with optional parameters

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
least astonishment in python: the mutable default argument

我想了解python __init__构造函数的行为和含义。当有一个可选参数,并且您试图将现有对象设置为新对象时,现有对象的可选值将被保留并复制。

看一个例子:

在下面的代码中,我试图用节点和可能多个子节点来构建树结构。在第一类NodeBad中,构造函数有两个参数,值和任何可能的子级。第二个类NodeGood只将节点的值作为参数。两者都有一个addchild方法来向节点添加子节点。

NodeGood类创建树时,它按预期工作。然而,当对NodeBad类执行相同的操作时,似乎一个孩子只能添加一次!

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

1
2
3
4
5
6
7
8
9
10
Good Tree
1
2
3
[< 3 >]
Bad Tree
1
2
2
[< 2 >, < 3 >]

阙葩萨?

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/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


问题是,可选参数的默认值只是一个实例。例如,如果您说def __init__(self, value, c=[]):,那么每次调用代码使用可选参数时,都会将相同的列表[]传递给方法。

所以基本上,您应该只对可选参数的默认值使用不可变的日期类型,如None。例如:

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

然后您可以在方法体中创建一个新的列表:

1
2
if c == None:
  c = []


可变的默认参数是混淆的根源。

看到这个答案:"最小的惊讶"和可变的默认参数