【问题标题】:Trying to Understand Weird Recursion/Object-Attribute/Scope "Gotcha" [duplicate]试图理解奇怪的递归/对象属性/范围“陷阱”[重复]
【发布时间】:2015-04-28 19:09:45
【问题描述】:

我只是花了几个小时在 python 中思考以下问题,但我无法弄清楚它为什么会发生。

假设我正在制作一个决策树类:

class DecisionTree:

    def __init__(self, name, children = dict()):
        self.name = name 
        self.children = children # <------- here's the problem

    def add_child(self, child_name, child_tree):
        self.children[child_name] = child_tree

    def pprint(self, depth = 0):
            for childkey in self.children:
                tabs = " ".join(["\t" for x in xrange(depth)])
                print tabs + 'if ' + self.name + ' is ' + str(childkey)
                if (self.children[childkey].__class__.__name__ == 'DecisionTree'): # this is a tree, delve deeper
                    print tabs + 'and...'
                    child = self.children.get(childkey)
                    child.pprint(depth + 1 )
                else:
                    val = self.children.get(childkey)
                    print tabs + 'then predict ' + str( val )
                    print "\n"
            return ''

现在让我们构建一棵废话树,并尝试打印它:

def make_a_tree(depth = 0, counter = 0):
    counter += 1

    if depth > 3:
        return 'bottom'
    else:

        tree = DecisionTree(str(depth)+str(counter))

        for i in range(2):
            subtree = make_a_tree(depth+1, counter)
            tree.add_child(i, subtree)
        return tree

foo = make_a_tree()
foo.pprint()

此代码导致无限递归循环,因为树结构(不知何故)错误地使用树的第二个节点引用自身来构建。

如果我将上面标记的行(第 5 行)更改为 tree.children = dict(),则一切正常。

我无法理解这里发生的事情。编写代码背后的意图是为“children”获取一个参数,如果没有传递,则创建一个空字典并将其用作子项。

我对 Python 还是很陌生,我正在努力让这成为一种学习体验。任何帮助将不胜感激。

【问题讨论】:

  • 在此处查看此答案:stackoverflow.com/questions/5587582/…
  • 旁白:child = self.children.get(childkey) / child.pprint(depth + 1 ) 看起来很奇怪。 .get 是一个字典方法,如果键不存在,则返回默认值(无,如果没有另外指定)。但如果钥匙不存在,None.pprint 将不起作用;并且由于您正在循环self.children 的键,我认为它必须在那里。所以self.children[childkey] 就足够了,或者你可以迭代self.children.items() 给出的对。
  • 谢谢——那只是我拼命调试时的痕迹,寻找解决问题的方法(我一度认为我的问题出在打印功能中……不知道为什么我想使用get 而不是括号会有​​所帮助)。

标签: python object recursion scope


【解决方案1】:

问题在于函数的默认参数(并且__init__() 也不例外)只创建一次。换句话说,每次创建新的DecisionTree 时,您都在重复使用相同的dict 对象。

你需要做的是这样的:

class DecisionTree:

def __init__(self, name, children = None):
    self.name = name
    if children is None:
        children = {}
    self.children = children

【讨论】:

    【解决方案2】:

    作为您的问题的解决方案,我建议将默认参数 children 初始化为 None,并有如下一行: self.children = children 或 dict()

    问题在于,在 Python 中,函数参数是通过引用传递的,并且具有可变值(并且字典是可变的)的默认参数在函数定义时(仅一次)被评估,而不是在每次调用函数时评估每次调用函数时 dict() 都会返回相同的引用。 常见的 python 陷阱。

    【讨论】:

    • 两个很好的答案。我随意将另一个标记为已接受,因为它排在第一位。
    猜你喜欢
    • 1970-01-01
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    相关资源
    最近更新 更多