python在list有但是无法引用_深拷贝


刷lc题的时候,老踩这种坑,索性搞懂。

先有概念:

1、python里面的所有元素都是对象,实例化一个对象就会开辟新的内存空间,然后返回一个引用给你。

2、a = 3。这里实例化了一个对象3,用变量名a表示。需要明确a不是对象本身,a只是这个对象3的引用。(回忆一下c++里面的指针和引用)

可变对象和不可变对象

可变对象和不可变对象的本质区别就是:对象本身的值可不可变。

可变对象:可变对象的值是可以原地 (inplace) 改变的,我们可以通过引用 (变量名) 来操作可变对象,使得它的值原地变化(即不开辟新的内存空间,地址不变)。

包括:list、set、dict(添加值我们不把它看成开辟“新”的内存空间,因为地址没变)。

不可变对象:不可变对象的值是不能改变的。如果引用 (变量名) 如果想要改变值,那不好意思,请你找新的对象去吧!(也就是会实例化新的对象,开辟新的内存空间,产生一个新的地址)

包括:int、str、float、tuple

特殊:int值比较小时[-5,257),会共享对象

Demo(此图出现太多,不明出处):


python在list有但是无法引用_不可变对象_02

不可变对象 int


python在list有但是无法引用_python 2维list_03

可变对象 list

+= 和 =+

这两个操作符,和可变/不可变对象有密切联系。

+=:调用对象的__iadd__方法,若不存在,才调用__add__方法;

=+:直接调用对象的__add__方法。

__iadd__方法直接在原地 (inplace) 修改对象的值,返回值为None,因此引用的地址不变;而__add__方法会返回一个新的对象,原对象不变。

和可变/不可变对象的联系:

一般情况下,可变对象有__iadd__方法,而不可变对象只有__add__方法。

另:对list而言,+= 和 extend() 等价。

Demo:


python在list有但是无法引用_不可变对象_04


坑1:python 解二叉树题遇到的问题

坑2:dp初始化二维数组时,下面注释里面那种写法是错的。


class


为什么呢?因为int是不可变对象,复制出来都是新的对象,所以第一维没有问题。而list复制的只是引用,第二维看似复制了很多次,实际上都指向同一个对象。

所以可以用*复制不可变对象,但千万不要用来复制可变对象!


python在list有但是无法引用_python 2维list_05

错误(左),正确(右)

深Copy和浅Copy

这篇文章写得很好,就不重复造轮子了。

张小鸡:5张图彻底理解Python中的浅拷贝与深拷贝zhuanlan.zhihu.com

python在list有但是无法引用_不可变对象_06


总结一下:

1、赋值

赋值操作,其实就是给一段内存地址贴标签。

对于不可变对象,=、copy、deepcopy三者等价。

2、浅拷贝

拷贝父对象的副本,但不会拷贝对象的内部的子对象。

或者说浅拷贝只是拷贝了原始元素的引用(内存地址),拷贝对象的值是否会随着被拷贝对象变化,仅取决于原始元素是可变对象还是不可变对象。

3、深拷贝

完全拷贝了父对象及其子对象。

深拷贝复制的不只是原始元素的引用,而是整个原始元素对象。因此,拷贝对象和被拷贝对象的值相互独立。

参考:

关于python中的+、+=、*、*=
Python 中 a+=b 和 a=a+b 的区别有哪些? - 刘志军的回答 - 知乎
Python中的可变对象与不可变对象
5张图彻底理解Python中的浅拷贝与深拷贝