python中的不可变对象

python中的不可变对象,python,variable-assignment,immutability,mutable,Python,Variable Assignment,Immutability,Mutable,我看到一篇关于不可变对象的文章 它说什么时候: 变量=不可变的 将不可变的赋值给变量 例如 a=b#b是不可变的 在这种情况下,a指的是b的副本,而不是指的是b。 如果b是可变的,a将是对b的引用 因此: 但在这种情况下: a = 10 b = 10 a is b # return True print id(10) print id(a) print id(b) # id(a) == id(b) == id(10) 如果a是10的副本,而b也是10的副本,那么为什么id(a)==id(b)=

我看到一篇关于不可变对象的文章

它说什么时候:
变量=不可变的

将不可变的赋值给变量

例如
a=b#b是不可变的

在这种情况下,
a
指的是
b的副本
,而不是
指的是b
。 如果b是可变的,
a将是对b的引用

因此:

但在这种情况下:

a = 10
b = 10
a is b # return True
print id(10)
print id(a)
print id(b) # id(a) == id(b) == id(10)

如果
a
10
的副本,而
b
也是
10
的副本,那么为什么
id(a)==id(b)==id(10)?

虽然这篇文章对某些语言可能是正确的,但对Python来说是错误的

在Python中执行任何常规赋值时:

你没有复制任何东西。您只是将名称指向赋值右侧的对象

可变性是不相关的

更具体地说,原因是:

a = 10
b = 10
a is b
True
,是
10
是interned吗?这意味着Python在内存中保留一个
10
,任何设置为
10
的对象都指向相同的
10

如果你这样做

a = object()
b = object()
a is b
您将得到
False
,但是

a = object()
b = a
a is b
仍然是
True

“简单”不可变文本(尤其是-1和255之间的整数)是内部的,这意味着即使绑定到不同的名称,它们仍然是相同的对象

>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True
In [4]: smalllist[0] = 3
In [5]: biglist
Out[5]: [[3, 1, 2]]

因为实习已经解释过了,所以我只讨论可变/不变的东西:

将不可变的赋值给变量

当谈到实际发生的事情时,我不会选择这样的措辞

我们有对象(存在于内存中的东西)和访问这些对象的方法:名称(或变量),它们“绑定”到引用中的对象。(可以说是指向对象的点)

这些名称/变量彼此独立,它们可以绑定到同一个对象,也可以绑定到不同的对象。重新定位一个这样的变量不会影响任何其他变量

没有按值传递或按引用传递这样的事情。在Python中,您总是通过“对象”传递/分配。在向函数赋值或传递变量时,Python从不创建副本,它总是传递/分配与您已经拥有的对象完全相同的对象

现在,当您尝试修改一个不可变对象时,会发生什么?如前所述,对象是不可变的,因此所发生的情况是:Python创建一个修改后的副本

至于你的例子:

a = 10
b = a
a =20
print (b) #b still is 10
这与易变性无关。在第一行,您将值为
10
的int对象绑定到名称
a
。在第二行,将
a
引用的对象绑定到名称
b

在第三行,您将值为
20
的int对象绑定到名称
a
,这不会更改名称
b
绑定到的内容

它说在这种情况下,a指的是b的副本,而不是b。如果b 是可变的,a将是对b的引用

如前所述,Python中没有引用这样的东西。Python中的名称绑定到对象。不同的名称(或变量)可以绑定到同一个对象,但不同名称之间没有联系。当你修改东西的时候,你就修改了对象,这就是为什么绑定到该对象的所有其他名称都“看到了变化”,它们绑定到了你修改过的同一个对象,对吗

如果将一个名称绑定到另一个对象,就会发生这种情况。其他名字没有变魔术,他们保持原样

关于列表示例:

In [1]: smalllist = [0, 1, 2] 
In [2]: biglist = [smalllist]    
In [3]: biglist
Out[3]: [[0, 1, 2]] 
而不是在[1]和[2]中,我可能会写:

In [1]: biglist = [[0, 1, 2]]
In [2]: smalllist = biglist[0]
这是等价的

这里要看到的重要一点是,biglist是一个包含一项的列表。当然,这一项是一个对象。它是一个列表这一事实并没有让人产生什么魔力,它只是一个简单的对象,恰好是一个列表,我们将它附加到名称
smalllist

因此,访问biglist[i]与访问smalllist完全相同,因为它们是同一个对象。我们从未复制过,我们传递了这个物体

In [14]: smalllist is biglist[0]
Out[14]: True
因为列表是可变的,所以我们可以更改smallist,并查看biglist中反映的更改。为什么?因为我们实际上修改了smallist提到的对象。我们仍然拥有相同的对象(除了它已更改的事实)。但biglist将“看到”这一变化,因为它的第一项引用的是同一个对象

>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True
In [4]: smalllist[0] = 3
In [5]: biglist
Out[5]: [[3, 1, 2]]
当我们将列表“加倍”时,情况也是如此:

In [11]: biglist *= 2
In [12]: biglist
Out[12]: [[0, 1, 2], [0, 1, 2]]
发生的情况如下:我们有一个列表:[object1,object2,object3](这是一个通用示例) 我们得到的是:[object1,object2,object3,object1,object2,object3]:它只会在列表末尾插入(即修改“biglist”)所有项目。同样,我们插入对象,而不是神奇地创建副本

因此,当我们现在更改biglist第一项中的一项时:

In [20]: biglist[0][0]=3
In [21]: biglist
Out[21]: [[3, 1, 2], [3, 1, 2]]

我们也可以更改
smalllist
,因为无论出于何种目的,
biglist
都可以表示为:
[smalllist,smalllist]
——它包含了两次相同的对象。

这并不能解释他文章的前三分之二,只有最后三分之一。@agf:最后三分之一是有问题的部分。仍然需要指出的是,他从他读到的文章中得出的结论是错误的。我并不反对措辞含糊不清。Python试图对
int
值进行内部处理,但并不总是成功。尝试
a=1000
b=999
,然后再尝试
b+=1
。当我刚才尝试时,
a
b
分别绑定到一个值为1000的不同
int
对象。不过,我认为对于值0和1,实习将始终成功。这种情况下:
lst=[0,1,2]*2
,列表