我正在编写一个应用程序,其中标签是可链接的,并且需要检索整个链接标签链。不允许自我引用。运行以下代码最终会产生一些非常奇怪的结果:
class Tag(object):
def __init__(self, name):
self.name = name
self.links = []
def __repr__(self):
return "<Tag {0}>".format(self.name)
def link(self, tag):
self.links.append(tag)
def tag_chain(tag, known=[]):
chain = []
if tag not in known:
known.append(tag)
print "Known: {0}".format(known)
for link in tag.links:
if link in known:
continue
else:
known.append(link)
chain.append(link)
chain.extend(tag_chain(link, known))
return chain
a = Tag("a")
b = Tag("b")
c = Tag("c")
a.link(b)
b.link(c)
c.link(a)
o = tag_chain(a)
print "Result:", o
print "------------------"
o = tag_chain(a)
print "Result:", o
结果:
Known: [<Tag a>]
Known: [<Tag a>, <Tag b>]
Known: [<Tag a>, <Tag b>, <Tag c>]
Result: [<Tag b>, <Tag c>]
------------------
Known: [<Tag a>, <Tag b>, <Tag c>]
Result: []
所以,不知何故,我不小心创建了一个闭包。据我所知,已知应该已经超出范围,并在函数调用完成后消失。
如果我将chain_tags()的定义更改为未设置默认值,则问题就会消失:
...
def tag_chain(tag, known):
...
o = tag_chain(a, [])
print "Result:", o
print "------------------"
o = tag_chain(a, [])
print "Result:", o
为什么会这样?
答案 0 :(得分:9)
这是Python中常见的错误:
def tag_chain(tag, known=[]):
# ...
known=[]
并不意味着如果已知未被提及,请将其设为空列表;事实上,它与“匿名”列表相关联。每次知道默认为该列表时,它都是相同的列表。
在这里做你想做的典型模式是:
def tag_chain(tag, known=None):
if known is None:
known = []
# ...
如果未提供,则将known
正确初始化为空列表。
答案 1 :(得分:3)
可能作为额外的eplanation会发生什么:默认参数只是存储在函数对象本身(即Py2中的tag_chain.func_defaults
)并用于在需要时扩展参数:
>>> def x(a=['here']):
... a.append(a[-1]*2)
...
>>> x
<function x at 0x0053DB70>
>>> x.func_defaults
(['here'],)
在此示例中,您可以观看默认列表在那里增长:
>>> x()
>>> x.func_defaults
(['here', 'herehere'],)
>>> x()
>>> x.func_defaults
(['here', 'herehere', 'herehereherehere'],)
修改默认参数有点像更改类变量。