从C/C++内存布局视野讨论python对象的复制和赋值行为

python中变量名本质上等价于C++的指针,基于这个认识就比较好理解python变量复制复制之间的规律了。

==============================================================
一、对对象直接赋值,可以认为让对象指针指向了其他内存块。因此原来两个对象指向同一块内存,直接对其中一个python变量赋值,不会修改此前指向同一块内存的python变量的值。
要理解python对象的复制和赋值行为,一定要理解python对象内存模型,如果理解了C++的指针,一切都很简单了。

在python中,变量等价于C++指针,而python对象中,复合对象中包含的对象都是用指针形式表达的。
如a=[1,2, [  3, 4, [
                             5, 6 
                          ]
               ]
        ]
要这么理解:
(1)a本身是指针,指向的内存对象本身包含3个元素,第一个1,第二个2,第三个是指针(指向另外一个内存对象)
(2)那个被指向的对象包含3个元素,即3,4以及一个指针,该指针指向另外一个内存对象
(3)2中提到的第三个元素指向的内存对象有两个元素,即5和6

有了上面的认识,我们来讨论一些python复制和赋值行为。特别补充,python中数组序号和C++一致,也是从0开始,我下面有些表述可能不严谨,“第三个元素”在C++中实际表述“第二个元素”,而“第一个元素”在C++中通常表述为“第0元素”。实际上python通常也是这么表述的,但是我懒得修改啦,哈哈哈

  
#------------------------------------------------------------------------------------------------------
>>> a=1                    # 对象a(指针a)指向了存储1的某个内存块
>>> b=a                   #  b=a, 相当于把a的地址赋给b,因此b和a指向同一块内存啦!
>>> a=2                    # a=2,本质上相当于指针a指向了新内存,而b指向内存地址没有变,因此b内容不变
>>> print(a,b)           #  修改b,不修改a,因为原生简单数据类型独立存储
2 1

#------------------------------------------------------------------------------------------------------
>>> a="hello"          
>>> b=a                 # b和a指向同一个内存字符串"hello"
>>> a="hi"             # a被指向了新的内存字符串"hi" ( b的指向还是字符串"hello")
>>> print(a,b)        # 所以a,b不变
hi hello

二、复杂数据对象,python的变量相当于C++的指针或者引用,因此a=[1,2,3],此后b=a, 相当于指针b,a指向同一个内存对象,因此修改了a[0]不但修改了a对应的内容,也修改了b对应内容

>>> a=[1,2,3]
>>> b=a
>>> a[0]=3
>>> print(a,b)
[3, 2, 3] [3, 2, 3]

==================================================================
三、两个变量指向同一块内存,其中一个变量通过成员函数修改变量得情况

a=[1,2,3]
b=a
a[0] = 3            #a[0]=3, 相当于调用a对象的[]运算符函数,对该内存块第一个元素操作,因此a变,b也变

==================================================================
四,他哪知“点心之中有点心”
#------------------------------------------------------------------------------------------------------
a=[1,2,3]
b=[4,5,6,a]   # b[3]存储的是a, a本身是个对象是指针
b[3]=9          # 把b[3]原来存储的地址值(a对应内存指针)修改成4,但是没有修改a指向内存块内容 
print(a,b)      # 因此b变,a不变,结果:a=[1, 2, 3]       b=[4, 5, 6, 9]

#------------------------------------------------------------------------------------------------------
a=[1,2,3]
b=[4,5,6,a]
b[3][0]=9     #b[3]实际就是a对象,b[3][0]相当于a[0], 因此修改了a对象指向内存第一个元素的值
print(a,b)     # 因此a,b 指向的内存块都发生了修改。

#------------------------------------------------------------------------------------------------------
a=[1,2,3]
c=[a,a,a]       #c指向内存块有三个元素,每个元素都是指针,这些指针指向同一块内存,存储【1,2,3】
a[0]=100      # 修改a指向内存块的第一个元素
print(a,c)      # 由于第一,第二,第三元素和a指向同一块内存,因此c最终c[0][0],c[1][0],c[2][0]都变成100
#------------------------------------------------------------------------------------------------------
# 此时,a=[100, 2, 3], 
#           c=[[100, 2, 3], [100, 2, 3], [100, 2, 3]]

#------------------------------------------------------------------------------------------------------
a=[1,2,3]
c=[a,a,a]
c[0][0]=100    #将第一个元素的指针指向的内存的第一个元素修改成100
print(a,c)         # 由于第一,第二,第三元素指向内存在同一块,因此c最终c[0][0],c[1][0],c[2][0]都变成100
#------------------------------------------------------------------------------------------------------
# 此时,a=[100, 2, 3], 
#           c=[[100, 2, 3], [100, 2, 3], [100, 2, 3]]

############################################################################
五、浅复制,copy()函数 【这儿想想,C++中类对象存在指针成员下的浅复制行为】
(1)浅复制是复制对象的第一层元素,如果是指针,就复制指针,因此两个对象的两个指针将必然指向同一块内存啦。(对于复杂结构,深层的元素复制不到)
(2)因此,修改原对象的最外层元素时,复制出来的新对象元素不发生改变,
(3)修改元对象的内层元素时,复制的新对象的内层元素随之发生改变。(正常啦,操作的是同一块内存啊)


a=[1,2, [3,4] ]   #a中本质上3个元素,第一个存储1,第二个存储2,第三个元素存储指针(假设p),
c=a.copy()       # 浅复制对象a, c中三元素1,2以及指针p,因此a和c的第三个元素都是p,指向同一内存
a[2][0] = 5        # a[2]返回指针p,p[0]得到p指向内存的头一个元素,
                         # 由于b中第三个元素也是指针p,p指向内存变了,打印b的值,必然和a相等
# 因此,此时打印a和c,都显示[1,2,[5,4]]
# 从结果上讲,  它和下面代码打印结果一致,但是注意,他们的内存布局可不一样!
a=[1,2, [3,4]]
c=a
a[2][0] = 5       # 因此,此时打印a和c,因为a,c两个指针本质上指向一个内存,都显示[1,2,[5,4]]


########################################################################3
六、深复制(和C++类对象的深复制完全一个概念,只是C++得自己实现,python中import copy包就可以了)
      深复制是复制对象中的所有元素,复制的是全新的对象,不在与原对象有任何关联。改变原对象不会对已经复制出来的新对象产生影响。

import copy as cp 
a=[1,2, [3,4] ]       #a中本质上3个元素,第一个存储1,第二个存储2,第三个元素存储指针(假设p),
c=cp.copy(a)       # 对a深复制,此时c中第3元素也是指针,但和a中第三元素指向内存的地址不一样,内容一样
a[2][0] = 5           # 修改a中第三元素指针对应内存第1元素的值,
                            # 由于深复制策略,a中第三元素指针值和b中第三元素对应指针值不一致,指向内存不同
                            # 因此操作后,a,c打印出来结果讲不一样啦,哈哈哈

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值