Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python类变量通过更改只应取其值的实例变量而改变_Python_Python 2.7_Class_Pass By Reference - Fatal编程技术网

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

我在初始化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 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(