前言

在写某个脚本时,用了DFS(深度有限遍历)算法判断两个点的可达性,并传入参数trace记录路径,如下因为直接采用赋值形式,导致函数逻辑错误(cur_trace改变的时候,trace也改变了),这才了解到在Python中存在赋值、浅拷贝和深拷贝的区别。

image-20220520184026023

相关概念

在了解赋值、深浅拷贝之前,先来了解以下概念:

  • 对象

    • 不可变对象:一旦创建就不可修改的对象,如数值(int, float)、字符串、元组是不可变对象。
    • 可变对象:对象本身可变,如列表、字典、集合。
  • 引用:在python中,新建一个变量都是在为某个对象新增一个引用,引用本身不占有内存空间,只是对对象的一个指向(别名)。如下图,在a=2中,2是一个对象,a是对2这个对象的一个引用。

    image-20220520190913544

赋值

  • 方法:“=”

  • 特点:赋值就是对对象的引用,不开辟新的内存空间。如下,b=a只是对a所指的对象增加一个引用b,a和b指向同一个对象。

    image-20220520212043032

  • 当改变b时:a也会改变。

浅拷贝

  • 方法

    • 切片操作:b = a[:]
    • 列表生成式:b = [ _ for _ in a]
    • 工厂函数:b = list(a)
    • copy()函数:b = a.copy()
  • 特点:开辟新内存空间,拷贝父对象,但不拷贝对象内部的子对象,子对象仍然是引用。如下,b=a.copy()后b只拷贝了a的第一层,元素是对象就拷贝了对象,元素是引用就拷贝了引用。

    image-20220520213151055

  • 当b改变时

    • b中对不可变对象的修改不影响a
    1
    2
    3
    b[0]=2
    print(b) # [2,[2,3,4]]
    print(a) # [1,[2,3,4]]
    • b中对可变对象的修改会导致a中也改变
    1
    2
    3
    b[1].append(5)
    print(b) # [1,[2,3,4,5]]
    print(a) # [1,[2,3,4,5]]

深拷贝

  • 方法:用copy模块中的deepcopy()函数,b = copy.deepcopy(a)

  • 特点:开辟新内存空间,递归拷贝对象全部内容,包括父对象及其子对象。

    image-20220520214631675

  • 当b改变时:改变b中父对象或子对象均不影响a,二者完全独立。

总结

其实其他语言如Java、JS等中也有这一概念,看了一下,其定义基本相同。最后总结一下赋值、浅拷贝和深拷贝区别:

类型 是否开辟新内存空间 子对象不可变时,改变子对象 子对象可变时,改变子对象
赋值 原数据也改变 原数据也改变
浅拷贝 原数据不改变 原数据也改变
深拷贝 原数据不改变 原数据不改变

参考文章