我在初始化Python类时遇到了一种奇怪的效果。不确定我是否忽视了一些明显的东西。
首先,我知道显然传递给类的列表是通过引用传递的,而整数是按值传递的,如下例所示:
class Test:
def __init__(self,x,y):
self.X = x
self.Y = y
self.X += 1
self.Y.append(1)
x = 0
y = []
Test(x,y)
Test(x,y)
Test(x,y)
print x, y
产生结果:
0 [1, 1, 1]
到目前为止一切顺利。现在看看这个例子:
class DataSheet:
MISSINGKEYS = {u'Item': ["Missing"]}
def __init__(self,stuff,dataSheet):
self.dataSheet = dataSheet
if self.dataSheet.has_key(u'Item'):
self.dataSheet[u'Item'].append(stuff[u'Item'])
else:
self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item']
像这样调用
stuff = {u'Item':['Test']}
ds = {}
DataSheet(stuff,ds)
print ds
DataSheet(stuff,ds)
print ds
DataSheet(stuff,ds)
print ds
的产率:
{u'Item': ['Missing']}
{u'Item': ['Missing', ['Test']]}
{u'Item': ['Missing', ['Test'], ['Test']]}
现在让我们打印MISSINGKEYS
:
stuff = {u'Item':['Test']}
ds = {}
DataSheet(stuff,ds)
print DataSheet.MISSINGKEYS
DataSheet(stuff,ds)
print DataSheet.MISSINGKEYS
DataSheet(stuff,ds)
print DataSheet.MISSINGKEYS
这会产生:
{u'Item': ['Missing']}
{u'Item': ['Missing', ['Test']]}
{u'Item': ['Missing', ['Test'], ['Test']]}
完全相同的输出。为什么呢?
MISSINGKEYS
是一个类变量,但它没有被故意改变。
在第一次通话中,课程进入这一行:
self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item']
显然开始了这一切。显然,我只希望self.dataSheet[u'Item']
获取self.MISSINGKEYS[u'Item']
的值,而不是成为对它的引用或类似内容。
在以下两个中调用
行self.dataSheet[u'Item'].append(stuff[u'Item'])
而是调用而append
适用于self.dataSheet[u'Item']
和self.MISSINGKEYS[u'Item']
上的ds == DataSheet.MISSINGKEYS
Out[170]: True
ds is DataSheet.MISSINGKEYS
Out[171]: False
。
这导致假设在第一次调用之后,两个变量现在引用相同的对象。
然而,虽然他们是平等的,但他们没有:
ds[u'Item'] is DataSheet.MISSINGKEYS[u'Item']
Out[172]: True
有人可以向我解释这里发生了什么以及如何避免它吗?
编辑: 我试过这个:
/foo
好吧,两个字典中的这一个条目都引用了同一个对象。我怎样才能分配值呢?
答案 0 :(得分:1)
下面:
else:
self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item']
您正在使用dataShee['Item']
的值列表设置MISSINGKEYS['Item']
。 相同的列表。尝试
else:
self.dataSheet[u'Item'] = list(self.MISSINGKEYS[u'Item'])
制作副本。
答案 1 :(得分:1)
思考Python函数调用中发生的事情"通过引用传递" vs"通过价值"通常不是很有用;有些人喜欢使用术语"通过对象"。请记住,Python中的所有内容都是一个对象,因此即使将一个整数传递给一个函数(在C术语中),您实际上也会将指针传递给该整数对象。
在您的第一个代码块中执行
self.X += 1
此不会修改绑定到self.X
的当前整数对象。它创建一个具有适当值的新整数对象,并将该对象绑定到self.X
名称。
然而,
self.Y.append(1)
你正在改变绑定到self.Y
的当前列表对象,这恰好是作为Test.__init__
传递给y
的列表对象1}}参数。这与调用代码中的y
列表对象相同,因此当您修改self.Y
时,您正在更改调用代码中的y
列表对象。 OTOH,如果你做了像
self.Y = ['new stuff']
然后名称self.Y
将绑定到新列表,旧列表(仍然在调用代码中绑定到y
)将不受影响。
您可能会发现这篇文章很有用:Facts and myths about Python names and values,由SO资深人士Ned Batchelder撰写。