Python还债日记之对象(二)
Python中对象的一些基础操作,赋值、拷贝等。
前文链接:
1. 对象的操作之对比
python中有两种对变量的比较方式,分别是 ==
以及调用 is
关键词,不同点在于:
-
==
只比较两个变量的值是否相等,相等则返回True -
is
则即比较两个变量值,又比较两个变量地址,都相等才返回True
is
等同于调用id
判断地址是否相等,再和操作==
判断值是否相等相与
2. 对象的操作之赋值
个人理解,在python中,赋值就是将对象的地址通过引用的方式传递给变量,同时对象的引用次数加1
赋值的内在操作是地址的传递,这个地址指对象个体
的地址,而不是子对象地址
赋值操作具有以下几点特征:
-
赋值与被赋值的两个对象,在python中可以认为是完全一样的,用一个变量给另一个变量赋值,其实就是给当前内存中的对象增加一个“标签”而已
1 2 3 4 5 6 7 8
>>> a = (11) >>> print(id(a)) 10914816 >>> b = a >>> print(id(b)) 10914816 >>> b is a True
-
对于可变对象,如List,赋值时不需要重新开辟空间(因为可变对象是可变的,赋值时相当于给对象添加了一个新的标签)
-
对于不可变对象,如Tuple、Set等,在赋值时需要开辟新的空间(因为需要破坏原有的引用,将变量的引用指向新的对象)但是python中的小对象整数池、短字符串缓冲区需要特殊考虑
-
对于可变对象,b是a的赋值,那么改变b的值,或者改变b的值,都会更新另外一方的值
3. 对象的操作之拷贝
拷贝操作有两种,在python中,只拷贝浅层对象的操作叫做浅拷贝,递归拷贝所有对象的操作叫做深拷贝
3.1 浅拷贝
python中的浅拷贝操作包括:copy 模块的 copy 函数 ,对象的 copy 函数 ,工厂方法,切片等
对于不可变对象,浅拷贝操作是传递引用,相当于赋值
对于可变对象,浅拷贝操作是对浅层对象进行拷贝,子对象不变
这里的浅层对象,对于字典、列表等类型来说,指的就是最外层的对象,也就是列表或者字典本身。可变对象的浅拷贝过程,实际上是在堆区分配了一块新内存,但是这块新内存内部的子对象,依旧是指向原内存中的位置的。
举例说明:
-
不可变对象的浅拷贝
1 2 3 4
>>> a = 555 >>> b = a >>> id(a), id(b) (140441428658224, 140441428658224)
因为是赋值的过程,所以内存地址一致,这时如果改变b,相当于一次新的赋值,a不会一起改变
-
可变对象的浅拷贝
1 2 3 4 5 6 7 8
>>> listA = [1, 'a', [1, 2]] >>> listB = listA.copy() >>> print(listA, listB) [1, 'a', [1, 2]] [1, 'a', [1, 2]] >>> id(listA), id(listB) (140441428382344, 140441428382408) >>> id(listA[0]), id(listB[0]) (10914496, 10914496)
因为是拷贝后的变量
listB
是新开辟的内存,所以listA
与listB
内存地址不一致这时如果改变
listB
,情况分为两种:-
添加、删除或改动不可变对象,
listA
都不会随着改变 -
改动可变对象,
listA
会一起改变,比如在listB
中第二个位置,向列表[1, 2]
中添加一个元素,那么listA
也会改变
-
3.2 深拷贝
Python中的深拷贝操作,只有copy模块的 deepcopy 函数
深拷贝也可以理解成递归拷贝,在Python中,深拷贝对于子对象中的可变对象进行复制,而不可变对象则沿用之前的引用。
深拷贝的最终结果就是,即使改变原来的变量,新变量也不会改变,二者互不影响
|
|
4. 传值还是传引用?
在函数传参时,我们都知道基本的参数传递机制有两种,传值以及传引用。我们先梳理一下传值以及传引用的异同,然后再分析python中是传值还是传引用。
首先回顾一下形参与实参的区别,形参是指被调用的函数的形式参数,其作为函数内部的局部变量。实参是指调用函数时,实际传递的参数。函数会在其对应的堆栈中开辟一块空间,来存放传递进来的实参的值。在这个过程中,值传递的特点是,对于形参的任何操作,都不会改变传递进来的实参的值。而引用传递,传递的是实参的引用,也可以理解为地址,即形参和实参指向同一个地址,这时改变形参,实参的值也会发生改变。
对于python中,前面我们分析了,由于其动态语言的特性,python中的变量都是引用,所以调用函数时,传递的都是引用!但是如果我们从改变形参实参是否发生改变的角度来分析,python中存在可变和不可变对象,所以对于不可变对象,形参改变,相当于重新开辟了空间以及再次进行引用的传递,并不会改变实参,相当于传值。对于可变对象,形参改变,指向的原对象也会改变,所以相当于传引用!
总结一下:
-
从传递参数自身的性质来分析,python属于传引用
-
从形参与实参的关系来分析,python中不可变对象是传值,可变对象是传引用
5. 总结
-
不可变对象在赋值时会开辟新空间
-
可变对象进行赋值前后,改变一个,另外一个也会改变
-
深浅拷贝对于不可变对象进行拷贝时,不开辟新空间,相当于赋值操作
-
浅拷贝拷贝的是浅层对象,深拷贝才会递归拷贝所有子对象(只针对可变对象才进行新建操作,直到最后所有对象都是不可变为止)
-
python默认使用的是浅拷贝,因为其速度快、占用空间小、效率高
-
python中的传值还是传引用,要辩解地分析,可变与不可变对象,其传递过程的本质不同。