不附配图,配图参见 https://blog.csdn.net/weixin_42137700/article/details/88022264 中的图类似
python不可变对象有 数字,元组,字符串
其他都是可变对象
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中的数据结构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中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会发生改变
再考察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不会发生改变
引用和view的区别在于引用改变shape会影响原数组,view不会,但是都可以更改原数据