Python深浅拷贝

文章目录

  • [1 概述](#1 概述)
  • [2 数据类型](#2 数据类型)
    • [2.1 可变类型](#2.1 可变类型)
    • [2.2 不可变类型](#2.2 不可变类型)
  • [3 深浅拷贝](#3 深浅拷贝)
    • [3.1 浅拷贝](#3.1 浅拷贝)
    • [3.2 深拷贝](#3.2 深拷贝)
  • [4 深浅拷贝对数据类型的影响](#4 深浅拷贝对数据类型的影响)
    • [4.1 对于不可变类型的影响](#4.1 对于不可变类型的影响)
    • [4.2 对于可变类型的影响](#4.2 对于可变类型的影响)
    • [4.3 总结](#4.3 总结)
  • [5 实现机制](#5 实现机制)
    • [5.1 copy](#5.1 copy)
    • [5.2 id](#5.2 id)
  • [6 示例](#6 示例)
    • [6.1 普通赋值](#6.1 普通赋值)
    • [6.2 浅拷贝可变类型](#6.2 浅拷贝可变类型)
    • [6.3 浅拷贝不可变类型](#6.3 浅拷贝不可变类型)
    • [6.4 深拷贝可变类型](#6.4 深拷贝可变类型)
    • [6.5 深拷贝不可变类型](#6.5 深拷贝不可变类型)
  • [7 注意事项](#7 注意事项)

1 概述

在 Python 中,可变类型和不可变类型的拷贝行为有所不同。理解它们的区别对于正确使用浅拷贝和深拷贝非常重要。

2 数据类型

在Python中,从对象的内存值是否可以被修改的角度来考虑,数据类型可分为可变类型和不可变类型。

2.1 可变类型

可变类型的对象在创建后,其内存中的值可以被修改,而对象的内存地址(引用)不变。常见类型包括列表(list)字典(dict)集合 (set)

2.2 不可变类型

对象一旦创建,其内存中的值就不能被修改。如果需要"修改"不可变对象,实际上是创建了一个新对象,并将变量(引用)指向新对象的内存地址。常见类型包括字符串 (str)元组 (tuple)数值类型(整型 int、浮点型 float)布尔类型 (bool)

3 深浅拷贝

在 Python 中,从对象及其嵌套对象的复制方式的角度来考虑,拷贝可分为浅拷贝和深拷贝。

3.1 浅拷贝

浅拷贝只复制对象本身,而不复制对象内部的嵌套对象。浅拷贝后的对象和原对象共享内部的嵌套对象。

3.2 深拷贝

深拷贝会递归复制对象及其所有嵌套对象。深拷贝后的对象和原对象完全独立,互不影响。

4 深浅拷贝对数据类型的影响

4.1 对于不可变类型的影响

对于不可变类型(如字符串 (str)元组 (tuple)数值类型(整型 int、浮点型 float)等),无论是浅拷贝还是深拷贝,都不会创建新对象,而是直接引用原对象。这是因为不可变对象的值不能被修改,共享是安全的。

4.2 对于可变类型的影响

对于对于可变类型(如列表、字典、集合等),深浅拷贝的行为会有所不同:

浅拷贝:创建一个新对象,但嵌套的可变对象仍然是共享的。

深拷贝:创建一个新对象,并递归复制所有嵌套的可变对象。

4.3 总结

类型 浅拷贝行为 深拷贝行为
不可变类型 与赋值相同,直接引用原对象(如果包含可变对象,则嵌套的可变对象共享) 与赋值相同,直接引用原对象(如果包含可变对象,则嵌套的可变对象共享)
可变类型 创建新对象,但嵌套的可变对象共享 创建新对象,并递归复制所有嵌套的可变对象

5 实现机制

5.1 copy

在 Python 中,浅拷贝和深拷贝是通过copy模块中的copy()deepcopy()函数实现的。

浅拷贝:copy模块的copy()
深拷贝:copy模块的deepcopy()函数

5.2 id

在 Python 中,可以通过id()函数获取对象的内存地址。id() 返回一个整数,表示对象的唯一标识符(通常是对象在内存中的地址)。这个地址在对象的生命周期内是唯一的,可以用来判断两个变量是否指向同一个对象。

6 示例

6.1 普通赋值

普通赋值,内存地址都一样

python 复制代码
# 不可变类型
num_1 = 8919
num_2 = num_1
print(f'id(num_1):{id(num_1)}')  # 2522975289840
print(f'id(num_2):{id(num_2)}')  # 2522975289840
print(f'id(8919):{id(8919)}')    # 2522975289840

# 可变类型
list_1 = ['辰南', '梦可儿', '龙舞']
list_2 = ['唤魔经', '太上忘情录', '通天动地魔功']
list_3 = [list_1, list_2]
list_4 = list_3
print(f'id(list_3):{id(list_3)}')  # 2522975191232
print(f'id(list_4):{id(list_4)}')  # 2522975191232

6.2 浅拷贝可变类型

浅拷贝只复制对象本身,而不复制对象内部的嵌套对象

python 复制代码
import copy

list_1 = ['辰南', '梦可儿', '龙舞']
list_2 = ['唤魔经', '太上忘情录', '通天动地魔功']
list_3 = [list_1, list_2, 8919, 12]
# 浅拷贝
list_4 = copy.copy(list_3)
print(f'id(list_3):{id(list_3)}')  # 对象本身地址不一样:1579186077888
print(f'id(list_4):{id(list_4)}')  # 对象本身地址不一样:1579183616704

print(id(list_2))     # 嵌套对象地址共享:1579183315072
print(id(list_3[1]))  # 嵌套对象地址共享:1579183315072

list_2[2] = '逆乱八式'  # 嵌套对象内容修改,引用对象内容同时发生改变
print(f'list_3:{list_3}')  # list_3:[['辰南', '梦可儿', '龙舞'], ['唤魔经', '太上忘情录', '逆乱八式'], 8919, 12]
print(f'list_4:{list_4}')  # list_4:[['辰南', '梦可儿', '龙舞'], ['唤魔经', '太上忘情录', '逆乱八式'], 8919, 12]

6.3 浅拷贝不可变类型

与赋值相同,直接引用原对象(如果包含可变对象,则嵌套的可变对象共享)

python 复制代码
import copy

tuple_1 = ('辰南', '梦可儿', '龙舞')
tuple_2 = ('唤魔经', '太上忘情录', '通天动地魔功')
tuple_3 = (tuple_1, tuple_2, 8919, 12)
tuple_4 = copy.copy(tuple_3)

print(f'id(list_3):{id(tuple_3)}')  # 地址不发生改变:1922101059136
print(f'id(list_4):{id(tuple_4)}')  # 地址不发生改变:1922101059136

print(f'id(tuple_2):{id(tuple_2)}')        # 共享地址:2341031630272
print(f'id(tuple_3[1]):{id(tuple_3[1])}')  # 共享地址:2341031630272

6.4 深拷贝可变类型

深拷贝会递归复制对象及其所有嵌套对象。深拷贝后的对象和原对象完全独立,互不影响。

python 复制代码
import copy

list_1 = ['辰南', '梦可儿', '龙舞']
list_2 = ['唤魔经', '太上忘情录', '通天动地魔功']
list_3 = [list_1, list_2, 8919, 12]
# 浅拷贝
list_4 = copy.deepcopy(list_3)
print(f'id(list_3):{id(list_3)}')  # 对象本身地址不一样:2252152333504
print(f'id(list_4):{id(list_4)}')  # 对象本身地址不一样:2252171929023

print(id(list_2))     # 嵌套对象地址共享:2252175821184
print(id(list_3[1]))  # 嵌套对象地址共享:2252175821184

list_1[2] = '李若兰'    # 对深拷贝对象不影响
list_2[2] = '逆乱八式'  # 对深拷贝对象不影响
print(f'list_3:{list_3}')  # [['辰南', '梦可儿', '李若兰'], ['唤魔经', '太上忘情录', '逆乱八式'], 8919, 12]
print(f'list_4:{list_4}')  # [['辰南', '梦可儿', '龙舞'], ['唤魔经', '太上忘情录', '通天动地魔功'], 8919, 12] 

6.5 深拷贝不可变类型

与赋值相同,直接引用原对象(如果包含可变对象,则嵌套的可变对象共享)

python 复制代码
import copy

tuple_1 = ('辰南', '梦可儿', '龙舞')
tuple_2 = ('唤魔经', '太上忘情录', '通天动地魔功')
tuple_3 = (tuple_1, tuple_2, 8919, 12)
tuple_4 = copy.deepcopy(tuple_3) # 深拷贝

print(f'id(list_3):{id(tuple_3)}')  # 地址不发生改变:2321897201216
print(f'id(list_4):{id(tuple_4)}')  # 地址不发生改变:2321897201216

print(f'id(tuple_2):{id(tuple_2)}')        # 共享地址:2321937124417
print(f'id(tuple_3[1]):{id(tuple_3[1])}')  # 共享地址:2321937124417

7 注意事项

  • 对于不可变类型,深浅拷贝的效果与赋值相同,因为不可变对象的值不能被修改。
  • 如果不可变对象包含可变对象(如元组中包含列表),则需要注意嵌套对象的共享问题。
  • 深拷贝可能会比较耗时,尤其是当对象嵌套层级很深或数据量很大时。
相关推荐
数据智能老司机1 天前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i1 天前
drf初步梳理
python·django
每日AI新事件1 天前
python的异步函数
python
这里有鱼汤1 天前
miniQMT下载历史行情数据太慢怎么办?一招提速10倍!
前端·python
databook2 天前
Manim实现脉冲闪烁特效
后端·python·动效
程序设计实验室2 天前
2025年了,在 Django 之外,Python Web 框架还能怎么选?
python
倔强青铜三2 天前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试