试图理解怪异 Recursion/Object-Attribute/Scope "Gotcha"
Trying to Understand Weird Recursion/Object-Attribute/Scope "Gotcha"
我刚刚花了几个小时思考 python 中的以下问题,我不太明白为什么会这样。
说我正在做决策树 class:
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" 取一个参数,如果传递了 none,则创建一个空字典并将其用作子字典。
我是 Python 的新手,我正在努力使它成为一种学习体验。任何帮助将不胜感激。
问题是函数的默认参数(__init__()
也不例外)只创建一次。换句话说,每次创建新的 DecisionTree
.
时,您都在重复使用相同的 dict
对象
你需要做的是:
class DecisionTree:
def __init__(self, name, children = None):
self.name = name
if children is None:
children = {}
self.children = children
作为您问题的解决方案,我建议将默认参数 children 初始化为 None,并有如下一行:
self.children = children 或 dict()
问题是在 Python 中,函数参数是通过引用传递的,具有可变值(和字典是可变的)的默认参数在函数定义时(仅一次)而不是每次都被评估调用函数导致每次调用 dict() 时都会 return 相同的引用。
常见 python 陷阱。
我刚刚花了几个小时思考 python 中的以下问题,我不太明白为什么会这样。
说我正在做决策树 class:
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" 取一个参数,如果传递了 none,则创建一个空字典并将其用作子字典。
我是 Python 的新手,我正在努力使它成为一种学习体验。任何帮助将不胜感激。
问题是函数的默认参数(__init__()
也不例外)只创建一次。换句话说,每次创建新的 DecisionTree
.
dict
对象
你需要做的是:
class DecisionTree:
def __init__(self, name, children = None):
self.name = name
if children is None:
children = {}
self.children = children
作为您问题的解决方案,我建议将默认参数 children 初始化为 None,并有如下一行: self.children = children 或 dict()
问题是在 Python 中,函数参数是通过引用传递的,具有可变值(和字典是可变的)的默认参数在函数定义时(仅一次)而不是每次都被评估调用函数导致每次调用 dict() 时都会 return 相同的引用。 常见 python 陷阱。