2010-11-03 60 views
3

我正在编写一个应用程序,其中标记是可链接的,并且需要检索整个链接标记链。不允许自引用。运行下面的代码有一些非常奇怪的结果结束了:为什么设置一个默认参数值使该函数关闭?

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 

这是为什么?

回答

9

这是Python中一个常见的错误:

def tag_chain(tag, known=[]): 
    # ... 

known=[]并不意味着,如果知道是未供给,使之成为空列表;实际上,它将已知的“匿名”列表绑定在一起。每次知道该列表的默认值时,它就是相同的列表。

典型模式做你打算在这里是什么,就是:

def tag_chain(tag, known=None): 
    if known is None: 
     known = [] 
    # ... 

如果没有提供它其正确初始化known空列表。

+0

感谢您的明确答复。我习惯Python比这更明确。如果有人知道这个决定背后的理由,我会非常有兴趣看到它。 – 2010-11-04 12:33:09

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'],) 

修改默认参数有点像更改类变量。

+0

非常有帮助。谢谢! – 2010-11-04 12:33:34

相关问题