【问题标题】:class getting kwargs from enclosing scope类从封闭范围中获取 kwargs
【发布时间】:2015-04-20 22:13:59
【问题描述】:

Python 似乎从类方法的封闭范围中推断出一些 kwargs,我不知道为什么。我正在实现一个 Trie:

class TrieNode(object):
  def __init__(self, value = None, children = {}):
    self.children = children
    self.value = value

  def __getitem__(self, key):
    if key == "":
        return self.value
    return self.children[key[0]].__getitem__(key[1:])

  def __setitem__(self, key, value):
    if key == "":
        self.value = value
        return
    if key[0] not in self.children:
        self.children[key[0]] = TrieNode()
    self.children[key[0]].__setitem__(key[1:], value)

在倒数第二行,我创建了一个新的 TrieNode,大概是一个空的子字典。但是,当我检查生成的数据结构时,树中的所有 TrieNode 都使用相同的子字典。即,如果我们这样做:

>>>test = TrieNode()
>>>test["pickle"] = 5
>>>test.children.keys()
['c', 'e', 'i', 'k', 'l', 'p']

而 test 的子节点应该只包含指向新 TrieNode 的“p”。另一方面,如果我们进入该代码的倒数第二行并将其替换为:

        self.children[key[0]] = TrieNode(children = {})

然后它按预期工作。那么,不知何故,self.children 字典被隐式地作为一个 kwarg 传递给 TrieNode(),但是为什么呢?

【问题讨论】:

    标签: python dictionary trie


    【解决方案1】:

    您遇到了mutable default argument 问题。将您的 __init__ 函数更改为这样

    def __init__(self, value=None, children=None):
        if not children:
            children = {}
    

    children 的默认值只会在函数创建时评估一次,而您希望它在每次调用中都是一个新的 dict。

    这是一个使用列表的问题的简单示例

    >>> def f(seq=[]):
    ...     seq.append('x') #append one 'x' to the argument
    ...     print(seq) # print it
    >>> f() # as expected
    ['x']
    >>> f() # but this appends 'x' to the same list
    ['x', 'x']
    >>> f() # again it grows
    ['x', 'x', 'x']
    >>> f()
    ['x', 'x', 'x', 'x']
    >>> f()
    ['x', 'x', 'x', 'x', 'x']
    

    正如我所链接的答案所描述的,这最终会咬住每个 Python 程序员。

    【讨论】:

    • 明确地说,Python 在解释时为默认值评估和分配内存位置一次;所有进一步的访问(运行时)只是查看该内存位置。如果你使用一个可变对象(list、dict),这就是问题出现的地方。
    【解决方案2】:

    您遇到的行为来自以下行:

    def __init__(self, value = None, children = {}):
    

    children = {} 称为mutable default argument。在这种情况下,默认参数是在函数定义中构造一次,并且每次修改都会影响以后的每个函数调用(使用默认值)。 要解决此问题,您应该将None 作为默认值传递(由于None 不可变,因此上述行为不适用):

    def __init__(self, value = None, children = None):
        self.children = children if children else {}
        self.value = value
    

    【讨论】:

    • 我喜欢self.children = children or {}
    猜你喜欢
    • 1970-01-01
    • 2013-11-01
    • 2019-01-06
    • 2015-01-10
    • 1970-01-01
    • 2014-04-02
    • 2011-01-28
    • 1970-01-01
    相关资源
    最近更新 更多