
在 Python 的编程世界里,数据处理常常涉及到复杂的数据结构,像列表、字典这类容器,甚至自定义对象。此时,深拷贝(deep copy)与浅拷贝(shallow copy)就发挥着关键作用。它们决定了数据在内存中的复制机制,别看只是复制方式的不同,却能对程序最终的运行结果产生天壤之别,深刻理解这两者,是掌握 Python 数据处理技巧的必经之路 。
浅拷贝:
1. 浅拷贝的定义:
浅拷贝,是 Python 中一种特定的数据复制方式。执行浅拷贝时,程序会生成一个全新的对象,原对象的内容会被原样 “搬移” 到新对象中。不过,这里有个关键细节:倘若原对象包含子对象,浅拷贝不会深入子对象内部,将其元素逐一复制,而是直接把原对象内部子对象的引用 “传递” 给新对象。这就导致了浅拷贝生成的新对象与原对象,在内存层面共享着内部的子对象。
2. 浅拷贝的实现方式
(1)使用 copy 模块的 copy() 函数
import copyoriginal_list = [1, 2, [3, 4]]shallow_copied_list = copy.copy(original_list)
(2)使用列表、字典等数据结构的工厂函数
original_list = [1, 2, [3, 4]]shallow_copied_list = list(original_list) # 列表的工厂函数
(3)使用切片操作(适用于列表)
original_list = [1, 2, [3, 4]]shallow_copied_list = original_list[:] # 切片操作
(4)使用字典的 copy() 方法
original_dict = {'a': 1, 'b': [2, 3]}shallow_copied_dict = original_dict.copy() # 字典的 copy() 方法
3.浅拷贝的特点
新瓶装旧酒:新对象与旧引用并存:浅拷贝会诞生一个崭新的对象,可别被表象迷惑,其内部子对象其实延续了原对象中子对象的引用。就好比新租了一间房子(新对象),家具(子对象)却还是从原来房子搬过来的,没有购置新家具,仅仅是挪了个地方摆放。
牵一发而动全身:子对象共享带来的连锁反应:当原对象内含可变子对象,诸如列表、字典这类,一旦对这些子对象动手脚,浅拷贝后的对象也会跟着 “变天”。想象一下,你和朋友合租一套房,公用一个冰箱(可变子对象),你往冰箱里放了变质食物,朋友打开冰箱时,也会受到影响。
轻装上阵:高效的浅拷贝:浅拷贝不会像深拷贝那样,深入子对象内部层层复制,因此在性能表现上更胜一筹。就像打包行李,浅拷贝只拿表面看得见的物品,不打开箱子检查内部小件,速度自然更快 。
4.浅拷贝的示例
示例 1:修改浅拷贝后的对象
import copyoriginal_list = [1, 2, [3, 4]]shallow_copied_list = copy.copy(original_list)# 修改浅拷贝后的对象shallow_copied_list[0] = 100shallow_copied_list[2][0] = 300print("Original List:", original_list)print("Shallow Copied List:", shallow_copied_list)
输出
Original List:Shallow Copied List:
修改 shallow_copied_list[0] 不会影响 original_list,因为这是对新对象本身的修改。
修改 shallow_copied_list[2][0] 会影响 original_list,因为内部的子列表是共享的。
示例 2:使用切片操作实现浅拷贝
original_list = [1, 2, [3, 4]]shallow_copied_list = original_list[:]# 修改浅拷贝后的对象shallow_copied_list[0] = 100shallow_copied_list[2][0] = 300print("Original List:", original_list)print("Shallow Copied List:", shallow_copied_list)
输出
Original List:Shallow Copied List:
示例 3:字典的浅拷贝
original_dict = {'a': 1, 'b': [2, 3]}shallow_copied_dict = original_dict.copy()# 修改浅拷贝后的字典shallow_copied_dict['a'] = 100shallow_copied_dict['b'][0] = 200print("Original Dict:", original_dict)print("Shallow Copied Dict:", shallow_copied_dict)
输出
Original Dict: {'a': 1, 'b': [200, 3]}Shallow Copied Dict: {'a': 100, 'b': [200, 3]}
解释:
修改 shallow_copied_dict['a'] 不会影响 original_dict,因为这是对新对象本身的修改。
修改 shallow_copied_dict['b'][0] 会影响 original_dict,因为内部的列表是共享的。
对象内部没有嵌套的可变对象:如果对象内部只包含不可变对象(如整数、字符串、元组等),浅拷贝是安全的。
性能要求较高:浅拷贝的性能比深拷贝更高,因为它不会递归复制子对象。
共享子对象是期望的行为:如果你希望拷贝后的对象和原对象共享某些子对象,浅拷贝是一个合适的选择。
6. 浅拷贝的注意事项
共享子对象的风险:如果原对象包含可变子对象,修改这些子对象会影响浅拷贝后的对象。如果不希望共享子对象,应该使用深拷贝。
不可变对象的特殊性:对于不可变对象(如整数、字符串、元组等),浅拷贝和深拷贝的行为是相同的,因为不可变对象不能被修改。
深拷贝:
在 Python 里,深拷贝(Deep Copy)堪称数据复制的 “深度探索者”。它会另起炉灶,生成一个全新对象,然后开启一场深入 “旅程”,将原对象内部的所有子对象都逐一复制。这一过程就像是精心复刻一座错综复杂的城堡,一砖一瓦都重新打造。如此一来,深拷贝后的对象与原对象毫无瓜葛,二者彻底独立。不管对哪个对象进行修改,都不会干扰到另一个。特别是在面对对象内部嵌套着可变对象的复杂场景时,深拷贝能确保每个副本都自给自足、互不影响,为数据处理提供了极高的安全性与独立性 。
1. 深拷贝的定义
2. 深拷贝的实现方式
使用 copy.deepcopy() 函数
# 导入copy模块,用于实现深拷贝功能import copy# 创建一个包含不同类型元素的原始列表,其中包含一个嵌套列表original_list = [1, 2, [3, 4]]# 使用copy模块的deepcopy函数对原始列表进行深拷贝# 这样得到的新列表与原始列表完全独立,互不影响deep_copied_list = copy.deepcopy(original_list)
3. 深拷贝的特点
完全独立:深拷贝后的对象与原对象完全独立,修改其中一个不会影响另一个。
递归复制:深拷贝会递归地复制对象内部的所有子对象,包括嵌套的可变对象。
性能较低:由于深拷贝需要递归复制所有子对象,因此它的性能比浅拷贝低,尤其是在处理大型或复杂的嵌套结构时。
4.深拷贝的示例
示例 1:修改深拷贝后的对象
# 导入copy模块,这个模块提供了复制对象的功能,这里主要用到深拷贝import copy# 创建一个原始列表original_list,列表中包含了不同类型的元素,其中还有一个嵌套的子列表original_list = [1, 2, [3, 4]]# 使用copy.deepcopy()函数对原始列表进行深拷贝,将结果存储在deep_copied_list变量中# 深拷贝会递归地复制列表中的所有元素,包括子列表,使得新列表与原列表完全独立deep_copied_list = copy.deepcopy(original_list)# 修改深拷贝后的列表的第一个元素,将其值从1改为100deep_copied_list[0] = 100# 修改深拷贝后的列表中嵌套子列表的第一个元素,将其值从3改为300deep_copied_list[2][0] = 300# 打印原始列表,查看其内容是否发生变化print("Original List:", original_list)# 打印深拷贝后的列表,展示修改后的结果print("Deep Copied List:", deep_copied_list)、
输出:
Original List:Deep Copied List:
修改 deep_copied_list[0] 不会影响 original_list,因为这是对新对象本身的修改。
修改 deep_copied_list[2][0] 也不会影响 original_list,因为深拷贝递归复制了内部的子列表,两个列表是完全独立的。
示例 2:嵌套字典的深拷贝
# 导入copy模块,用于实现深拷贝功能。# 在Python中,copy模块提供了浅拷贝和深拷贝的方法,这里我们使用深拷贝。import copy# 创建一个原始字典original_dict,这个字典包含了一个人的信息。# 其中,'name'键对应一个字符串值,'details'键对应另一个字典,# 这个子字典又包含了'age'(年龄,数值类型)和'hobbies'(爱好,列表类型)两个键值对。original_dict = {'name': 'Alice','details': {'age': 25,'hobbies': ['reading', 'traveling']}}# 使用copy.deepcopy()函数对原始字典进行深拷贝,# 将拷贝后的结果存储在deep_copied_dict变量中。# 深拷贝会递归地复制字典中的所有元素,包括嵌套的子字典和列表,# 使得新字典与原字典完全独立。deep_copied_dict = copy.deepcopy(original_dict)# 修改深拷贝后的字典中的值。# 首先,将深拷贝字典中'details'子字典的'age'值从25改为30。deep_copied_dict['details']['age'] = 30# 然后,向深拷贝字典中'details'子字典的'hobbies'列表中添加一个新的爱好'cooking'。deep_copied_dict['details']['hobbies'].append('cooking')# 打印原始字典,查看其内容是否发生变化。print("Original Dict:", original_dict)# 打印深拷贝后的字典,展示修改后的结果。print("Deep Copied Dict:", deep_copied_dict)
输出:
Original Dict: {'name': 'Alice', 'details': {'age': 25, 'hobbies': ['reading', 'traveling']}}Deep Copied Dict: {'name': 'Alice', 'details': {'age': 30, 'hobbies': ['reading', 'traveling', 'cooking']}}
修改 deep_copied_dict 中的嵌套字典和列表不会影响 original_dict,因为深拷贝递归复制了所有子对象。
示例 3:自定义对象的深拷贝
# 导入copy模块,以便使用深拷贝功能import copy# 定义Person类,用于表示人物对象class Person:# 构造函数,初始化对象的name和friends属性def __init__(self, name, friends):self.name = nameself.friends = friends# 定义对象的字符串表示形式,方便打印输出def __repr__(self):return f"Person(name={self.name}, friends={self.friends})"# 创建一个Person对象person1,名字为Alice,朋友列表为Bob和Charlieperson1 = Person("Alice", ["Bob", "Charlie"])# 使用深拷贝创建person2,它是person1的一个完全独立副本person2 = copy.deepcopy(person1)# 修改深拷贝后的person2对象的属性# 将person2的名字改为Eveperson2.name = "Eve"# 向person2的朋友列表中添加一个新的朋友Davidperson2.friends.append("David")# 打印原始的person1对象,查看其属性是否发生变化print("Original Person:", person1)# 打印深拷贝后的person2对象,展示修改后的属性print("Deep Copied Person:", person2)
输出
import copy# 定义Person类,用于表示人物对象class Person:def __init__(self, name, friends):self.name = nameself.friends = friendsdef __repr__(self):return f"Person(name={self.name}, friends={self.friends})"# 创建原始的Person对象person1person1 = Person("Alice", ["Bob", "Charlie"])# 对person1进行深拷贝,得到person2person2 = copy.deepcopy(person1)# 修改深拷贝后的person2对象的属性person2.name = "Eve"person2.friends.append("David")# 以更清晰的格式打印原始对象和深拷贝后的对象print("原始人物信息:")print(f"对象:{person1}")print("\n深拷贝后的人物信息:")print(f"对象:{person2}")
修改 person2 的 name 和 friends 不会影响 person1,因为深拷贝递归复制了所有属性
5. 深拷贝的适用场景
需要完全独立的副本:当对象内部包含嵌套的可变对象时,深拷贝可以确保副本与原对象完全独立。
复杂的数据结构:如嵌套的列表、字典、自定义对象等。
避免副作用:在函数中传递复杂对象时,深拷贝可以避免意外修改原对象。
6. 深拷贝的注意事项
性能开销:深拷贝需要递归复制所有子对象,因此在处理大型或复杂的嵌套结构时,性能开销较大。
循环引用问题:如果对象之间存在循环引用(如对象A引用对象B,对象B又引用对象A),深拷贝可能会导致栈溢出或无限递归。Python的 copy.deepcopy() 函数已经处理了循环引用问题,但在自定义深拷贝逻辑时需要注意。
7. 深拷贝的实现原理
深浅拷贝的实际应用:
1. 数据处理与修改
import copy# 原始数据original_data = {'name': 'Alice','scores': [90, 85, 88],'details': {'age': 25, 'city': 'New York'}}# 深拷贝数据copied_data = copy.deepcopy(original_data)# 修改拷贝后的数据copied_data['scores'][0] = 95copied_data['details']['city'] = 'San Francisco'# 原始数据不受影响print("Original Data:", original_data)print("Copied Data:", copied_data)
数据备份与恢复。
数据预处理(如修改数据后用于机器学习模型训练,而不影响原始数据)。
2. 配置管理
import copy# 默认配置default_config = {'debug': False,'database': {'host': 'localhost','port': 3306}}# 创建多个独立配置config_1 = copy.deepcopy(default_config)config_2 = copy.deepcopy(default_config)# 修改配置config_1['database']['host'] = '192.168.1.1'config_2['debug'] = Trueprint("Config 1:", config_1)print("Config 2:", config_2)
多环境配置(开发、测试、生产)。
动态生成多个独立的配置。
3. 对象复制与状态管理
在面向对象编程中,对象可能包含嵌套的对象或复杂的状态。如果你需要复制一个对象并确保新对象的状态独立于原对象,深拷贝是必要的。
import copyclass Player:def __init__(self, name, level):self.name = nameself.level = levelself.inventory = []def add_item(self, item):self.inventory.append(item)# 创建玩家对象player1 = Player("Alice", 10)player1.add_item("Sword")player1.add_item("Shield")# 深拷贝玩家对象player2 = copy.deepcopy(player1)# 修改拷贝后的对象player2.name = "Bob"player2.add_item("Bow")# 查看两个对象的状态print(f"Player 1: {player1.name}, {player1.inventory}")print(f"Player 2: {player2.name}, {player2.inventory}")
游戏开发中复制角色或物品。
状态快照与恢复(如撤销操作)。
4. 避免副作用
def process_data(data):# 浅拷贝数据以避免修改原始数据data_copy = data.copy()data_copy.append("Processed")return data_copyoriginal_data = [1, 2, 3]result = process_data(original_data)print("Original Data:", original_data)print("Result:", result)
函数式编程中避免副作用。
数据处理管道中确保数据独立性。
深拷贝:适用于需要完全独立副本的场景,如数据处理、配置管理、对象复制等。
浅拷贝:适用于性能敏感的场景,或者当对象内部没有嵌套结构时。



