我是Python的新手,在使用它编写一个小应用程序时遇到了一个问题。由于我在谷歌上找不到任何类似的东西,我可能会误解一些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()
方法时进行调试时kvals
参数KeyValList
构造函数包含前两对。但那怎么可能呢?我没有为这个kvals
参数提供任何内容,因此,由于参数默认值,它不应该初始化为空列表吗?
PS:我在Windows上使用Python 2.7.3。
答案 0 :(得分:2)
这基本上是一个非常常见的例子 - 首先是反直觉的 - Python的特征mutable default argument。要点是默认参数是函数对象的属性,因此如果你在一个函数调用中改变它们,你就会为所有函数调用改变它们。
此示例比标准的可变默认参数问题稍微复杂一些,因为您还将相同的对象作为类的属性和实例传递。但是代码中只有一个列表对象 - 由[]
创建的列表对象 - 因此通过实例属性进行变更与在函数对象上进行变异相同。
换句话说,当你写
self.kvals.append(...)
您将某些数据附加到特定列表对象。哪个列表对象?由[]
在函数定义中创建的函数,因此与使用Python作为函数的默认参数的函数相同。
答案 1 :(得分: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
this blogpost中对此问题有一个很好的解释。