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 注意事项

  • 对于不可变类型,深浅拷贝的效果与赋值相同,因为不可变对象的值不能被修改。
  • 如果不可变对象包含可变对象(如元组中包含列表),则需要注意嵌套对象的共享问题。
  • 深拷贝可能会比较耗时,尤其是当对象嵌套层级很深或数据量很大时。
相关推荐
喜欢理工科3 分钟前
18 C语言标准头文件
c语言·python·算法·c语言标准头文件
PacosonSWJTU8 分钟前
python基础-07-模式匹配与正则表达式
python·mysql·正则表达式
程序员总部11 分钟前
单例模式在Python中的实现和应用
开发语言·python·单例模式
测试盐25 分钟前
django入门教程之cookie和session【六】
后端·python·django
冷琴199626 分钟前
基于python+django的商城网站-电子商城管理系统源码+运行
开发语言·python·django
右恩38 分钟前
jupyter使用过程中遇到的问题
ide·python·jupyter
zhyoobo2 小时前
使用 Python 训练自己的 AI 模型:从数据预处理到深度学习
人工智能·python·深度学习
hylreg2 小时前
ROS2 部署大语言模型节点
人工智能·python·语言模型
十八只兔2 小时前
Jupyter Notebook :美化读取到的JSON格式的数据(以表格形式呈现)
python·jupyter·json·tabulate·python数据读取