Python类变量通过更改只应取其值的实例变量而改变
我在初始化Python类时遇到了一个奇怪的效果。不确定我是否忽略了一些明显的东西 首先,我知道传递给类的列表是通过引用传递的,而整数是通过值传递的,如本例所示:Python类变量通过更改只应取其值的实例变量而改变,python,python-2.7,class,pass-by-reference,Python,Python 2.7,Class,Pass By Reference,我在初始化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 class Da
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
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']
产生结果:
0 [1, 1, 1]
到目前为止还不错。现在看看这个例子:
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
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
好吧,这两个字典中的一个条目引用了同一个对象。我如何分配值呢?此处:
else:
self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item']
您正在使用MISSINGKEYS['Item']
的值列表设置dataShee['Item']
。同样的清单。试一试
制作副本。此处:
else:
self.dataSheet[u'Item'] = self.MISSINGKEYS[u'Item']
您正在使用MISSINGKEYS['Item']
的值列表设置dataShee['Item']
。同样的清单。试一试
制作副本。从“按引用传递”与“按值传递”的角度思考Python函数调用中发生的事情通常是没有用的;有些人喜欢用“路过物体”这个词。请记住,Python中的所有内容都是一个对象,因此即使将整数传递给函数(用C术语),实际上也传递了指向该整数对象的指针 在您的第一个代码块中
self.X += 1
这不会修改绑定到self.X
的当前整数对象。它使用适当的值创建一个新的整数对象,并将该对象绑定到self.X
名称
鉴于
self.Y.append(1)
您正在对绑定到self.Y
的当前列表对象进行变异,该对象恰好是传递给Test.\uuuuuu init\uuuu
作为其Y
参数的列表对象。这是调用代码中相同的y
列表对象,因此当您修改self.y
时,您正在更改调用代码中的y
列表对象。奥托,如果你做了这样的任务
self.Y = ['new stuff']
然后名称self.Y
将绑定到新列表,而旧列表(在调用代码中仍然绑定到Y
)将不受影响
您可能会发现这篇文章很有帮助:,这篇文章是由经验丰富的Ned Batchelder撰写的。考虑Python函数调用中“按引用传递”与“按值传递”的情况通常是没有用的;有些人喜欢用“路过物体”这个词。请记住,Python中的所有内容都是一个对象,因此即使将整数传递给函数(用C术语),实际上也传递了指向该整数对象的指针 在您的第一个代码块中
self.X += 1
这不会修改绑定到self.X
的当前整数对象。它使用适当的值创建一个新的整数对象,并将该对象绑定到self.X
名称
鉴于
self.Y.append(1)
您正在对绑定到self.Y
的当前列表对象进行变异,该对象恰好是传递给Test.\uuuuuu init\uuuu
作为其Y
参数的列表对象。这是调用代码中相同的y
列表对象,因此当您修改self.y
时,您正在更改调用代码中的y
列表对象。奥托,如果你做了这样的任务
self.Y = ['new stuff']
然后名称self.Y
将绑定到新列表,而旧列表(在调用代码中仍然绑定到Y
)将不受影响
您可能会发现这篇文章很有帮助:,它是由经验丰富的Ned Batchelder撰写的。
self.dataSheet[u'Item']=self.MISSINGKEYS[u'Item']
创建了一个引用,因此当您更改时,您可以到处更改它。您需要创建一个副本self.dataSheet[u'Item']=list(self.MISSINGKEYS[u'Item'])
yes。这是关于Python的,我不理解,99.9%的时候你不需要有意识地创建任何东西的副本,所有东西都像按值调用一样工作,然后你突然遇到了这样的事情。有了一个可变对象,你就创建了一个可以更改的引用,只有在对象不可变的情况下,如果使用i=12进行更改,才会创建新对象;b=i;i+=12
b仍然是12,因为int是不可变的,但是对于可变结构,更改是在适当的位置完成的,因此不会创建新对象。基本上,如果你想使用一个可变值/obect,而不仅仅是一个引用,你需要根据对象进行复制或者可能是deepcopy。谢谢,我想我现在明白了。不用担心,你也应该知道,即使对象本身是不可变的,如果它包含不可变对象,那么这些对象仍然可以更改,并且不可变对象仍然是相同的对象,即t=(1,[2]);t=t2;t[1]。追加(2)
,这就是deepcopy的用武之地。self.dataSheet[u'Item']=self.MISSINGKEYS[u'Item']
创建了一个引用,因此当您更改时,您可以到处更改它。您需要创建一个副本self.dataSheet[u'Item']=list(