第
Python赋值逻辑的实现
目录摘要:第一章引例第二章Python的反直觉第三章回答第一章的问题
摘要:
如果你学过C语言,那么当你初见Python时可能会觉得Python的赋值方式略有诡异:好像差不多,但又好像哪里有点不太对劲。
本文比较并解释了这种赋值逻辑上的差异。回答了为什么需要这种赋值逻辑以及如何使用这种赋值逻辑的问题。
当然,即使未学过C语言,也可通过本文更好地了解Python的赋值逻辑这种赋值逻辑影响着Python的方方面面,从而可以让你更好地理解和编写Python程序。
第一章引例
先来看一组似乎矛盾的代码:
#代码1
a=3
b=a
b=5
3
这看上去似乎很好理解。第二步中,a只是把值复制给b,然后b又被更新为5,a和b是两个独立的变量,那么a的值当然不会受到影响。
真的是这样吗?
再来看一段代码:
#代码2
a=[1,2,3]
b=a
b[0]=1024
[1024,2,3]
第二步中,a只是复制把列表复制给b,然后更新b[0]的值,最后输出a,可是a竟然也被改变了。
按照代码1的逻辑(即变量之间独立),代码2的中的a不应该受到影响。
为什么出现了这样的差异?
第二章Python的反直觉
先不解释上面那个看似矛盾的问题。
先来看看另一组简单的Python代码在内存中是什么样子的:
#代码3
b=3
b=b+5
它在内存中的操作示意图是这样的:
然而,从代码的的字面意思上看,把3赋给b,把b加5之后再赋给b。
也就是把代码看成这个样子:
b←3b←b+5
所以下面这张在内存中的操作图可能更符合我们的直觉:
也即b+5的值又写回到b中。典型的C程序就是这样的。为变量b分配一个int型的内存单元,然后将整数3存放在该内存单元中。b就代表了该块内存空间,不再移动,可以更新b的值,但b在内存中的地址就不再变化了。所以我们说b=b+5,就等于bb+5,把b的值加5之后还依然放入b中。变量b和它所在内存空间紧紧绑定在一起,人形合一。
而再看看上面Python中的内存示意图,b+5得到了一个新值,然后令b指向了这个新值。换句话说,它做的是事情是这样的:
b→3
b→b+5
先令b指向3,再令b指向b+5这个新值。
C程序更新的是内存单元中存放的值,而Python更新的是变量的指向。
C程序中变量保存了一个值,而Python中的变量指向一个值。
如果说C程序是通过操纵内存地址而间接操作数据(每个变量固定对应一个内存地址,所以说操纵变量就是操纵内存地址),数据处于被动地位,那么Python则是直接操纵数据,数据处于主动地位,变量只是作为一种引用关系而存在,而不再拥有存储功能。
在Python中,每一个数据都会占用一个内存空间,如b+5这个新的数据也占用了一个全新的内存空间。
Python的这种操作让数据成为主体,数据与数据之间直接进行交互。
而数据在Python中被称为对象(Object)。
这句话并不太严谨。不过在这个简单的例子中是成立的。
一个整数3是一个int型对象,一个hello是一个字符串对象,一个[1,2,3]是一个列表对象。
Python把一切数据都看成「对象」。它为每一个对象分配一个内存空间。一个对象被创建后,它的id就不再发生变化。
id是identity的缩写。意为身份;标识。
在Python中,可以使用id(),来获得一个对象的id,可以看作是该对象在内存中的地址。
一个对象被创建后,它不能被直接销毁。因此,在上个例子中,变量b首先指向了对象3,然后继续执行b+5,b+5产生了一个新的对象8,由于对象3不能被销毁,则令b指向新的对象8,而不是用对象8去覆盖对象3。在代码执行完成后,内存中依然有对象3,也有对象8,变量b指向了对象8。
如果没有变量指向对象3(即无法引用它了),Python会使用垃圾回收算法来决定是否回收它(这是自动的,不需要程序编写者操心)。
一个旧的对象不能被覆盖,因旧的对象交互而新产生的数据会放在新的对象中。也就是说每个对象是一个独立的个体,每个对象都有自己的主权。因此,