• 从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打印出来结果讲不一样啦,哈哈哈

  • 相关阅读:
    内网渗透知识 ——(一)、工作组、域、域控、活动目录
    布隆过滤器Bloom Filter
    wifi感知技术
    ConstraintLayout布局扩展
    Django学习笔记-实现联机对战(下)
    JVM面试核心点
    项目管理工具中的线性进度表是什么
    图像分割 - Hough变换圆环检测
    程序员保密协议(双向)
    linux 使用 navicat 报错的各种问题
  • 原文地址:https://blog.csdn.net/zxg519/article/details/126489134