一文读懂Python中的赋值操作、浅拷贝和深拷贝的区别

1. 可变对象和不可变对象

为了更好地理解Python中的深拷贝、浅拷贝以及赋值操作的区别,首先得熟悉Python中的对象类型。Python中的类型可以分为可变类型不可变类型两种:

  • 不可变对象

    不可变对象的值一旦定义就不能更改,若强行改变不可变对象的值,由于其值是不可变的,则会重新分配一块内存,将新的值存入这块新的内存中,再将变量指向新的内存地址。常见的不可变类型包括元组数值类型字符串

    a = 1
    id1 = id(a)
    
    a = 2   # 改变不可变对象a的值
    id2 = id(a)
    print(id1 == id2)
    

    请添加图片描述

  • 可变对象

    与不可变对象相反,可变对象的值可以直接修改而不改变其在内存中的地址,即可以进行原地修改,可变类型包括字典列表自定义类

    a = [1, 2, 3]
    id1 = id(a)
    
    a[0] = 1
    id2 = id(a)
    print(id1 == id2)
    

    请添加图片描述

  • 复杂对象

    对于有可变对象和不可变对象嵌套而来的复杂对象而言,仍然遵循上述的规律:

    a = [1, 2, [3, 4]]
    id1 = [id(_) for _ in a]
    print(f'id1: {id1}')
    
    
    a[0] = 2
    a[2].append(5)
    id2 = [id(_) for _ in a]
    print(f'id2: {id2}')
    

    请添加图片描述

2. 赋值操作、深拷贝与浅拷贝

在Python中,赋值操作深拷贝以及浅拷贝可变类型不可变类型值的表现是不一样的

  • 2.1 不可变类型中的情况

    在不可变类型中,比较简单,三种操作都不会开辟新的内存空间:

    import copy
    
    a = 1
    b = a   # 赋值操作
    c = copy.copy(a)    # 浅拷贝
    d = copy.deepcopy(a)    # 深拷贝
    
    print(f'id(a) equals id(b)? {id(a) == id(b)}')
    print(f'id(a) equals id(c)? {id(a) == id(c)}')
    print(f'id(a) equals id(d)? {id(a) == id(d)}')
    
    a = 2
    print(f'a is {a}')
    print(f'b is {b}')
    print(f'c is {c}')
    print(f'd is {d}')
    

    一文读懂Python中的赋值操作、浅拷贝和深拷贝的区别_第1张图片

    可以看出,变量a, b, c, d的地址都是相同的,但是由于不可变类型的特殊性质,虽然四个变量具有相同的地址,但是当改变变量a的值时,会新开辟内存来存储新值,并将变量的指向更新到新内存。

  • 2.2 可变类型中的情况

    在可变类型中,赋值操作不会开辟新的内存空间,而浅拷贝深拷贝会开辟新的内存,所以对a的改变不会影响到c和d:

    import copy
    
    a = [1, 2, 3]
    b = a   # 赋值操作
    c = copy.copy(a)    # 浅拷贝
    d = copy.deepcopy(a)    # 深拷贝
    
    print(f'id(a) equals id(b)? {id(a) == id(b)}')
    print(f'id(a) equals id(c)? {id(a) == id(c)}')
    print(f'id(a) equals id(d)? {id(a) == id(d)}')
    
    a.append(4)
    print(f'a is {a}')
    print(f'b is {b}')
    print(f'c is {c}')
    print(f'd is {d}')
    

    一文读懂Python中的赋值操作、浅拷贝和深拷贝的区别_第2张图片

    那么,浅拷贝深拷贝有什么区别呢,在上述的单一的可变类型这种简单的情况下,似乎看不出来两种拷贝方式的区别,但在下述的复杂类型中就可以看出他们的区别了。

  • 复杂类型中的情况

    在复杂类型中(指基础类型的嵌套),浅拷贝深拷贝遵循以下规律:

    • 对于最外层的数据结构,无论是浅拷贝还是深拷贝都会开辟新的内存空间;
    • 对于非最外层的数据结构,浅拷贝会直接使用原始数据的内存空间,而深拷贝会为所有层的数据都开辟内存空间;
    • 无论那一层数据,赋值操作都不会开辟新的内存。
    import copy
    
    a = [1, 2, [3, 4]]
    b = a   # 赋值操作
    c = copy.copy(a)    # 浅拷贝
    d = copy.deepcopy(a)    # 深拷贝
    
    print(f'id(a) equals id(b)? {id(a) == id(b)}')
    print(f'id(a) equals id(c)? {id(a) == id(c)}')
    print(f'id(a) equals id(d)? {id(a) == id(d)}')
    
    a_item_id = [id(_) for _ in a]
    b_item_id = [id(_) for _ in b]
    c_item_id = [id(_) for _ in c]
    d_item_id = [id(_) for _ in d]
    print(f'a_item_id: {a_item_id}')
    print(f'b_item_id: {b_item_id}')
    print(f'c_item_id: {c_item_id}')
    print(f'd_item_id: {d_item_id}')
    
    a.append(4)
    a[2].append(100)
    
    print(f'a is {a}')
    print(f'b is {b}')
    print(f'c is {c}')
    print(f'd is {d}')
    

    一文读懂Python中的赋值操作、浅拷贝和深拷贝的区别_第3张图片

    如上结果所示,c[2]的内存地址和a[2]相同,说明浅拷贝不会为内存的数据开辟新的内存空间,而d[2]的内存地址和a[2]不同,正好说明了深拷贝会为内层的数据开辟新的内存,故a[2]的改变会影响到b[2]和c[2],不会影响到d[2]。

你可能感兴趣的:(Python,python,开发语言)