谈谈 python 中的赋值(绑定)操作

谈到python的赋值,我们需要将它与C、C++或Java等语言的赋值操作区别开来。
因为在python中,与其说是赋值,不如说是对象的绑定。
具体如何实现绑定操作,我们将在下面的文章中进行讨论。

在讨论绑定操作前,我们需要理解python中可变的(mutable)和不可变的(immutable)数据类型。

1. 可变类型、不可变类型

在python中,每个数据对象都可分为可变类型和不可变类型。
在核心类型中,数字、字符串和元组属于不可变类型;
列表和字典则是可变类型。

对于不可变类型,任何修改数据的操作都会产生一个新的对象。
而对于可变类型,我们可以直接在原对象上进行修改操作。

2. 赋值(绑定)操作

在python中,赋值符号 = 的作用是将等号右边的对象绑定到等号左边的变量名。
例如:

>>> x = 5
>>> y = x

第一条语句会在内存中创建一个数字对象5(如果没有存在的话),然后把它绑定到变量名x。
第二条语句是将x所绑定的对象5绑定到变量名y,这么一来,数字对象5就被绑定到了两个变量名x和y上。
也就是说,x和y指向的是同一个数字对象。

我们可以通过python内置的id(object)函数来查看对象的全局唯一标识符,从而判断不同变量名所绑定的对象是否是同一个。

>>> id(x)
49182304
>>> id(y)
49182304

可以看到,x和y所指向的对象具有相同的id,因此是同一个对象。

2.1 不可变类型的赋值操作

对于不可变类型的对象来说,每一次赋值都会创建一个新的对象,然后将新的对象绑定到原变量名上。
例如:

>>> x = 3
>>> id(x)
49182328
>>> x += 2
>>> id(x)
49182304

第一条赋值语句先创建了一个数字对象3,然后再把3绑定到变量名x。
第二条赋值语句创建了一个新的数字对象5,然后把5绑定到变量名x。
也就是说,python并不是简单的修改原来的变量值,而是创建了一个新的对象后重新绑定变量名。

再来看一个字符串的赋值操作:

>>> s = 'hello'
>>> id(s)
49766336
>>> s += ' world'
>>> id(s)
54347744

与数字类型一样,由于字符串也是不可变类型,因此对字符串的赋值操作同样会创建新的对象,然后再绑定到原变量名上。

2.2 可变类型的赋值操作

对于可变类型来说,如果是直接使用 = 对变量名赋新的值,同样会在内存中创建新的对象。

例如:

>>> mylist = [1, 2, 3]
>>> id(mylist)
54326936
>>> mylist = [4, 5, 6]
>>> id(mylist)
54369816

但是如果使用的是其它赋值语句,形如 +=*= 或者列表和字典自带的方法等,Python就会在原数据对象的基础上进行修改,不会创建新对象。

>>> mylist = [1, 2]
>>> id(mylist)
54326936
>>> mylist += [3, 4]
>>> id(mylist)
54326936

同样,对字典对象进行合并:

>>> mydict = {1:'a', 2:'b'}
>>> id(mydict)
49130208
>>> mydict.update({3:'c'})
>>> id(mydict)
49130208

可以看到,对于可变对象的操作会直接修改原数据对象,这种特性可以使python更快地进行赋值,在数据量大的时候比较明显。但与此同时可能会对开发者带来负面影响,也就是浅拷贝问题。

3. 浅拷贝、深拷贝

由于python特殊的赋值方式,也就是绑定操作,可能使得一个对象绑定了多个变量名。对于可变类型来说,在这种情况下,如果我们修改了其中的某个变量,就会无意中导致其它变量的值同时被修改,例如:

>>> x = [1, 2, 3]
>>> y = x
>>> y.append(4)
>>> x
[1, 2, 3, 4]

这就是前面提到的浅拷贝(Shallow Copy)问题,python为我们提供了解决这一问题的方法,导入copy库并调用其中的deepcopy函数即可。

语法: copy.deepcopy(x) 返回一个新的对象,该对象与x的值相同。

例如:

>>> import copy
>>> x = [1, 2]
>>> y = copy.deepcopy(x)
>>> y.append(3)
>>> x
[1, 2]
>>> y
[1, 2, 3]

通过使用 copy 库中的 deepcopy 函数来创建一个新的对象,当我们修改这一对象时,原变量的值不再受到影响,这样浅拷贝的问题就迎刃而解了。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注