• Python里的引用与拷贝规律


    python的可变不可变与各种浅拷贝深拷贝规则,一并梳理。

    Python一切皆引用

    在C++/Java里,int a = 1就是创建变量为a,赋值为1;int b = a就是创建变量b,赋值为a的值。a与b是毫不相干的,即“变量是盒子”,但是这不利于理解Python中的一个变量定义。在Python里,我们把变量视为“一个实际存储的引用”(图源:《流畅的python》)。

    image

    所以在python里,a = [1, 2, 3]先分配一块区域写入[1,2,3],再让a来代表它;b = a让b与a代表了同一个东西,即使a本身消失了(比如del a),也仅仅是撕下来一张标签而已,b仍然可以访问这个列表。其他类型也是如此

    情况一:直接引用

    image

    直接引用即b = a,正如上文所说,不会发生拷贝,只是让b也来代表a代表的区域。此时b就是a,b[0]也就是a[0]。

    如果修改了a,等于让a指向其他对象,与列表无关,所以b没有变化;而如果修改a[0](或者使用+=,append等),则修改了列表,b[0]也在变化。

    image

    但对于单个数或者元组字符串这种不可变对象,你也可以使用+=,但是他们不支持原地修改,因此实际上会调用a = a + b得到的是一个新对象。如a = (1, 2, 3); b = a; a += (4, 5),此时执行a = a + (4, 5),已经指向新的值了,所以b不会改变。

    情况二:浅复制

    有些时候我们只编辑列表或字典的副本,所以需要复制,一般最常见的复制方法有:

    b = a[:]
    b = list(_ for _ in a)
    b = copy(a)
    b = a.copy()
    

    这些都叫做浅复制,浅复制的时候发生了什么?
    image

    浅复制的逻辑将创建一个新对象,然后将每一个值复制一份放入新对象里,花费线性时间。可以看到复制后b与a完全一致,但是a is b不再成立了,a[0]和b[0]也是不再相关的值,你可以任意修改列表b,都不会影响到a里的四个元素(红蓝橙绿四个小圆)。

    情况三:深复制

    但是浅复制仍然有不能解决的问题。我们知道python里一切皆引用,图里的小圆不是盒子而是标签!,虽然a与b本身已经分开了,但如果有一个元素仍然是列表,那他们其实还是联系在一起的。
    image
    如图,浅复制时执行了b[1]=a[1],但b[1]和a[1]是引用,因此通过他们访问的仍然是同一个可变序列,修改a[1]不会导致b[1]变化,但修改a[1][0]却导致b[1][0]变化。

    所以我们引入深复制解决这个问题:

    from copy import deepcopy
    a = [1, [1, 2, 3], "hello"]
    b = deepcopy(a)
    

    深复制的逻辑是,将每一个值复制放进新一个对象里,而如果这一项也表示一个可变的迭代对象(列表,字典,没有特殊定制的自定义类),就将这个对象也复制一份。这样就可以得到一份完全的拷贝。

    image

  • 相关阅读:
    工厂方法模式
    简单工厂模式
    页面滚动动画
    JAVA的深浅拷备
    雷哥架构师课程大钢
    字节与字符串转换
    商城图片懒加载
    MYSQL数据库类型与JAVA类型对应表
    微信支付二码生成办法
    项目中dubbo的标准配置
  • 原文地址:https://www.cnblogs.com/ofnoname/p/16102663.html
Copyright © 2020-2023  润新知