Python class 变量通过更改应该只取其值的实例变量而改变
Python class variable getting altered by changing instance variable that should just take its value
初始化 Python class 时,我遇到了奇怪的效果。不确定我是否忽略了一些明显的东西。
首先,我知道传递给 classes 的列表显然是通过引用传递的,而整数是通过值传递的,如本例所示:
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
是一个 class 变量,但绝不会被故意更改。
在第一次调用中,class 进入这一行:
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'] = list(self.MISSINGKEYS[u'Item'])
制作副本。
根据 "pass by reference" 和 "pass by value" 思考 Python 函数调用中发生的情况通常没有用;有些人喜欢使用 "pass by object" 这个词。请记住,Python 中的所有内容都是对象,因此即使将整数传递给函数(用 C 术语),您实际上也是在传递指向该整数对象的指针。
在你的第一个代码块中
self.X += 1
这个 不会 修改绑定到 self.X
的当前整数对象。它创建一个具有适当值的新整数对象,并将该对象绑定到 self.X
名称。
而
self.Y.append(1)
你是改变绑定到self.Y
的当前列表对象,它恰好是传递给Test.__init__
的列表对象作为它的y
参数。这与调用代码中的 y
列表对象相同,因此当您修改 self.Y
时,您正在更改调用代码中的 y
列表对象。 OTOH,如果你做了像
这样的作业
self.Y = ['new stuff']
那么名称 self.Y
将绑定到新列表,而旧列表(在调用代码中仍绑定到 y
)将不受影响。
您可能会发现这篇文章很有帮助:Facts and myths about Python names and values,由 SO 资深人士 Ned Batchelder 撰写。
初始化 Python class 时,我遇到了奇怪的效果。不确定我是否忽略了一些明显的东西。
首先,我知道传递给 classes 的列表显然是通过引用传递的,而整数是通过值传递的,如本例所示:
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
是一个 class 变量,但绝不会被故意更改。
在第一次调用中,class 进入这一行:
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'] = list(self.MISSINGKEYS[u'Item'])
制作副本。
根据 "pass by reference" 和 "pass by value" 思考 Python 函数调用中发生的情况通常没有用;有些人喜欢使用 "pass by object" 这个词。请记住,Python 中的所有内容都是对象,因此即使将整数传递给函数(用 C 术语),您实际上也是在传递指向该整数对象的指针。
在你的第一个代码块中
self.X += 1
这个 不会 修改绑定到 self.X
的当前整数对象。它创建一个具有适当值的新整数对象,并将该对象绑定到 self.X
名称。
而
self.Y.append(1)
你是改变绑定到self.Y
的当前列表对象,它恰好是传递给Test.__init__
的列表对象作为它的y
参数。这与调用代码中的 y
列表对象相同,因此当您修改 self.Y
时,您正在更改调用代码中的 y
列表对象。 OTOH,如果你做了像
self.Y = ['new stuff']
那么名称 self.Y
将绑定到新列表,而旧列表(在调用代码中仍绑定到 y
)将不受影响。
您可能会发现这篇文章很有帮助:Facts and myths about Python names and values,由 SO 资深人士 Ned Batchelder 撰写。