2013-04-20 83 views
4

我是Python新手,在使用它编写小应用程序时,遇到了一个问题。由于我在Google上找不到任何类似的东西,我可能会误解Python的一些基本概念。如果你们中的某个人能向我解释这一点,那将是非常棒的。重构用于新对象的构造函数参数?

所以这里是一个最小的工作示例。请忽略类和变量名称,这只是一个人为的例子,但它显示了我的意思。你可以将它写入你的IDE并为你自己运行。

class State(object): 
    def __init__(self, wrappers = []): 
     self.wrappers = wrappers 

    def add(self, name, items): 
     lst = KeyValList(name) 
     for item in items: 
      lst.add(item, items[item]) 
     self.wrappers.append(Wrapper(lst)) 

class Wrapper(object): 
    def __init__(self, kv_lst): 
     self.kv_lst = kv_lst 

class KeyValList(object): 
    def __init__(self, name, kvals = []): 
     self.name = str(name) 
     self.kvals = kvals 

    def add(self, key, value): 
     self.kvals.append(KeyVal(key, value)) 

class KeyVal(object): 
    def __init__(self, key, val): 
     self.key = str(key) 
     self.val = val 

好的,那就是我使用的类的定义。
右下面我写了以下内容:

state = State() 
kv = { "key1" : "1", "key2" : "2", "key3" : "3" } 
state.add("1st", kv) 
kv = { "keyX" : "X", "keyY" : "Y", "keyZ" : "Z" } 
state.add("2nd", kv) 

for wrap in state.wrappers: 
    print wrap.kv_lst.name, "-->", 
    for kv in wrap.kv_lst.kvals: 
     print "%s:%s" % (kv.key, kv.val), 
    print "" 

作为一个Java程序员,我会想到以下的输出:

1st --> key3:3 key2:2 key1:1 
2nd --> keyZ:Z keyY:Y keyX:X 

然而,这一方案在Python我得到以下输出,而不是:

1st --> key3:3 key2:2 key1:1 keyZ:Z keyY:Y keyX:X 
2nd --> key3:3 key2:2 key1:1 keyZ:Z keyY:Y keyX:X 

为什么两个列表都包含所有键/值对?

我希望这个问题某处KeyValList类,因为在调试时state.add()方法被调用在主程序中的第2次的KeyValList构造函数的参数kvals包含前者对。但是,这怎么可能呢?我不提供任何东西给这个kvals参数,不应该因为参数默认值而被初始化为空列表吗?

PS:我在Windows上使用Python 2.7.3。

+3

[可变默认参数](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) – katrielalex 2013-04-20 22:19:36

回答

1

而不是使用可变默认参数,使用没有作为占位符,并让您的函数/方法在每次调用时构造新的容器。

更改此:

class State(object): 
    def __init__(self, wrappers = []): 
     self.wrappers = wrappers 

要这样:

class State(object): 
    def __init__(self, wrappers = None): 
     if wrappers is None: 
      wrappers = [] 
     self.wrappers = wrappers 

有问题的this blogpost一个很好的解释。

+1

非常感谢,这对我帮助很大。您提供的链接也很好地解释了“问题”。当我读它时,我首先想到Python中的这很奇怪,但稍后会解释为什么它确实有意义。谢谢。 – Matthias 2013-04-20 22:40:37

2

这基本上是一个非常常见的例子 - 而且起初颇为违反直觉 - Python的特征是mutable default argument。要点是缺省参数是函数对象的属性,因此如果您在一个函数调用中对它们进行变异,则可以将它们变为所有函数调用。

这个例子比标准的可变默认参数问题稍微复杂一点,因为你也传递相同的对象作为类的属性和实例。但是代码中只有一个列表对象 - 由[]创建的 - 因此通过实例属性对其进行变异与在函数对象上对其进行变异相同。

换句话说,当你写

self.kvals.append(...) 

要追加一些数据到一个特定列表对象。哪个列表对象?在函数的定义中由[]创建的一个,因此它与Python用作函数的默认参数的一样。

+0

谢谢,这当然解释了我的情况是什么问题。我现在必须将它记入我的大脑,以便为未来记住它:) – Matthias 2013-04-20 22:39:03