【发布时间】:2016-09-23 10:28:29
【问题描述】:
我在初始化 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
好吧,这两个字典中的这个条目引用了同一个对象。我怎样才能只分配值?
【问题讨论】:
-
self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item']创建一个引用,因此当您更改它时,您可以在任何地方更改它。您需要创建一个副本self.dataSheet[u'Item'] = list(self.MISSINGKEYS[u'Item']) -
是的。这是关于 Python 的东西我不明白,99.9% 的时间你永远不需要有意识地创建任何东西的副本,一切都像你做按值调用一样工作,然后你突然绊倒了这样的事情.
-
使用可变对象,您正在创建对可以更改的内容的引用,只有当对象是不可变的时才会创建一个新对象,如果使用
i = 12; b = i; i += 12b 进行了更改,那么仍然会为 12,因为 int 是不可变的,但具有可变结构,更改已就地完成,因此不会创建新对象。基本上,如果您想使用可变值/对象并且不只是想要引用,则需要根据对象进行复制或深度复制。 -
谢谢,我想我现在明白了。
-
不用担心,你也应该知道,即使对象本身是不可变的,如果它包含不可变对象,那么这些对象仍然可以更改,不可变对象仍然是同一个对象,即@987654342 @,这就是 deepcopy 的用武之地。
标签: python python-2.7 class pass-by-reference