从源码角度对python深浅拷贝的推测理解(不是源码分析,推测源码是怎么写的)

从源码角度对python深浅拷贝的推测理解(不是源码分析,推测源码是怎么写的)

不附配图,配图参见 https://blog.csdn.net/weixin_42137700/article/details/88022264 中的图类似

python与不可变对象

  1. python不可变对象有 数字,元组,字符串
    其他都是可变对象

  2. python一切皆对象

既然python一切皆是对象,而C语言中与对象对等的数据结构是Struct,假设这个结构叫做Struct PyObject

以数字1为例猜测其在内存中的存储方式如下:

对应一个Struct PyObject A,这个结构体中有指向内容为1的指针,还有一系列东西

猜测在python中作赋值语句 a=1 时,其实就是定义了Struct PyObject* a = &A;

因此a=1,b=1或b=a时a和b都是指向A的指针,因此两者一样。

形象理解就是内存中某一块存储了python中的对象1,1用在赋值语句右边时,就会将自身内存首地址给出去,因此等于1的变量实际上都是指向Struct PyObject A的指针。

当a=1,b=a,b=2时发生了什么呢,b此时变成了指向对象2的指针,而a不变还是指向1的指针,所以改变b不会改变a

python赋值引用

考察python中的数据结构list列表,推测其是C中的链表数据结构

那么a = [1, 2, 3], b = a, b[0] = 4的时候发生了什么呢

a是该链表头结点,占用内存地址0xAAAA,假设内存排列如下:

0xA, 0xB, 0xC是内存中三块地址,里面分别存储了指向对象1, 2, 3的结构指针,并且0xA的下一节点是0xB, 0xB的下一节点是0xC

又由于a和b都是头结点因此都等于0xAAAA,b[0] = 4发生的就是0xA中的内容变成指向对象4的结构指针

那么此时a是头结点,其内容还是0xA, 0xB, 0xC,但由于0xA中内容已经发生改变,所以a中内容也已经改变

python浅拷贝

考察python中list的copy,a = [1, 2, 3], b = a.copy(), b[0] = 4发生了什么呢

假设0xA, 0xB, 0xC是内存中三块地址,里面分别存储了指向对象1, 2, 3的结构指针,a是头结点指向0xA,

b由于是浅拷贝,python语法是说只拷贝顶层对象,也就是对对象1, 2, 3都做了拷贝(这一句其实是有问题的,不是这么理解,正确的理解是拷贝指向1, 2, 3的指针)

因此此时有内存地址0xAA, 0xBB, 0xCC分别存储了指向对象1, 2, 3的结构指针,b是头结点指向0xAA,

此时b[0] = 4发生的是0xAA中的指针指向了对象4,此时a对应的内容内存地址是0xA, 0xB, 0xC内容并未发生更改

浅拷贝

再考察a = [[1, 2, 3], 4, 5, 6], b = a.copy(), b[0][0] = 7,此时发生了什么呢

由于浅拷贝只拷贝顶层对象,也就是[1, 2, 3]的头结点和4, 5, 6的地址,看一下内存情况

a,
假设0xA, 0xB, 0xC是分别指向对象1, 2, 3,其头结点是0xAA指向0xA,0xD, 0xE, 0xF分别指向对象4, 5, 6
b,
0xd, 0xe, 0xf分别指向对象4, 5, 6,0xaa指向0xA

此时改变b[0][0]也就是改变了0xA的指向,a的内容0xaa还是指向0xA所以a会发生改变

python深拷贝

再考察a = [[1, 2, 3], 4, 5, 6], b = a.deepcopy(), b[0][0] = 7,此时发生了什么呢

由于深拷贝拷贝所有对象,看一下内存情况

a,
假设0xA, 0xB, 0xC是分别指向对象1, 2, 3,其头结点是0xAA指向0xA,0xD, 0xE, 0xF分别指向对象4, 5, 6
b,
0xd, 0xe, 0xf分别指向对象4, 5, 6,0xaa指向0xa, 0xa, 0xb, 0xc分别指向1, 2, 3

此时改变b[0][0]也就是改变了0xA的指向,a的内容0xaa还是指向0xa所以a不会发生改变

numpy拷贝问题

引用和view的区别在于引用改变shape会影响原数组,view不会,但是都可以更改原数据

你可能感兴趣的:(python)