Python构造函数和默认值

164

在下面的Node类中,一些变量(wordListadjacencyList)被所有Node实例共享。

>>> class Node:
...     def __init__(self, wordList = [], adjacencyList = []):
...         self.wordList = wordList
...         self.adjacencyList = adjacencyList
... 
>>> a = Node()
>>> b = Node()
>>> a.wordList.append("hahaha")
>>> b.wordList
['hahaha']
>>> b.adjacencyList.append("hoho")
>>> a.adjacencyList
['hoho']

有没有办法让构造函数参数保持默认值(在这种情况下为空列表),但使得ab都拥有自己的wordListadjacencyList变量?

我正在使用Python 3.1.2。

4个回答

187

可变的默认参数通常不会像您想象的那样工作。相反,请尝试以下方法:

class Node:
     def __init__(self, wordList=None, adjacencyList=None):
        if wordList is None:
            self.wordList = []
        else:
             self.wordList = wordList 
        if adjacencyList is None:
            self.adjacencyList = []
        else:
             self.adjacencyList = adjacencyList 

41
这些也可以用一行代码表示:self.wordList = wordList if wordList is not None else [],或者稍微不太安全的写法self.wordList = wordList or [] - Josh Bleecher Snyder
9
许多东西除了 None 之外也会被视为假值(例如 False0""{}(), 还有指定了 __nonzero__() 方法的对象)。前者是具体的:None 是唯一会让默认值为 [] 的特殊对象。而后者会将所有假值替换为 [] - Josh Bleecher Snyder
9
Michael J. Barber的贡献是正确的-但未提供解释。这种行为的原因是默认参数在函数定义时被绑定,而不是运行时。请参阅https://dev59.com/9nNA5IYBdhLWcg3wAItP - A---
8
那么,这是一个漏洞吗?这种行为相当反直觉。 - sharshofski
5
@sharshofski:这是一个糟糕的设计决策,我不知道还有哪种语言像这样处理默认值,但它并没有被正式视为错误。 - user2357112
显示剩余2条评论

38

让我们说明这里发生了什么:

Python 3.1.2 (r312:79147, Sep 27 2010, 09:45:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
...     def __init__(self, x=[]):
...         x.append(1)
... 
>>> Foo.__init__.__defaults__
([],)
>>> f = Foo()
>>> Foo.__init__.__defaults__
([1],)
>>> f2 = Foo()
>>> Foo.__init__.__defaults__
([1, 1],)

你可以看到默认参数存储在一个元组中,这是所涉及的函数的属性。这实际上与所涉及的类无关,并适用于任何函数。在Python 2中,该属性将是func.func_defaults

正如其他帖子中指出的那样,您可能希望使用None作为哨兵值,并给每个实例分配自己的列表。


28
class Node:
    def __init__(self, wordList=None adjacencyList=None):
        self.wordList = wordList or []
        self.adjacencyList = adjacencyList or []

20

我建议尝试:

self.wordList = list(wordList)

强制它复制而不是引用相同的对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接