4

我是 Python 新手,在使用它编写一个小型应用程序时遇到了一个问题。由于我在 Google 上找不到类似的东西,我可能误解了 Python 的一些基本概念。如果你们当中有人能向我解释一下,那就太好了。

所以这是一个最小的工作示例。请忽略类名和变量名,也就是说只是一个人为的例子,但它说明了我的意思。您可以将其 c&p 到您的 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()kvalsKeyValListkvals

PS:我在 Windows 上使用 Python 2.7.3。

4

2 回答 2

2

这基本上是 Python 的一个非常常见的——起初相当违反直觉的——特性的一个实例,可变的默认参数。要点是默认参数是函数对象的属性,因此,如果您在一个函数调用中对它们进行变异,您将对所有函数调用进行变异。

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

换句话说,当你写

self.kvals.append(...)

您正在将一些数据附加到特定的列表对象。哪个列表对象?由函数定义中创建的那个[],因此与 Python 用作函数的默认参数的那个相同。

于 2013-04-20T22:22:56.670 回答
1

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

改变这个:

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

在这篇博文中有一个很好的解释。

于 2013-04-20T22:21:42.070 回答