前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >14.Python深拷贝和浅拷贝详解

14.Python深拷贝和浅拷贝详解

作者头像
全栈若城
发布于 2025-04-12 11:13:48
发布于 2025-04-12 11:13:48
148033
代码可运行
举报
文章被收录于专栏:若城技术专栏若城技术专栏
运行总次数:33
代码可运行

基本概念

对象引用

Python中,变量实际上是对象的引用。当我们将一个变量赋值给另一个变量时,实际上是创建了一个新的引用指向同一个对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 简单的对象引用示例
x = [1, 2, 3]
y = x  # y引用了与x相同的列表对象

# 修改y会影响x
y.append(4)
print(f"x: {x}")  # 输出: x: [1, 2, 3, 4]
print(f"y: {y}")  # 输出: y: [1, 2, 3, 4]

# 验证x和y指向同一个对象
print(f"x和y是否是同一个对象: {x is y}")  # 输出: True
浅拷贝(Shallow Copy)

浅拷贝创建一个新对象,但它包含的是对原始对象中元素的引用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import copy

# 创建浅拷贝的几种方法
original = [1, [2, 3], {'a': 4}]

# 1. 使用copy模块
shallow_copy1 = copy.copy(original)

# 2. 使用列表的copy()方法
shallow_copy2 = original.copy()

# 3. 使用切片操作
shallow_copy3 = original[:]

# 验证浅拷贝的特性
print(f"原始对象: {original}")
print(f"浅拷贝1: {shallow_copy1}")
print(f"浅拷贝2: {shallow_copy2}")
print(f"浅拷贝3: {shallow_copy3}")

# 修改嵌套对象
original[1][0] = 'modified'
print(f"\n修改后的原始对象: {original}")
print(f"修改后的浅拷贝1: {shallow_copy1}")  # 嵌套列表也被修改
深拷贝(Deep Copy)

深拷贝创建一个新对象,并递归地复制原始对象中的所有嵌套对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 创建深拷贝
original = [1, [2, 3], {'a': 4}]
deep_copy = copy.deepcopy(original)

# 修改原始对象的嵌套元素
original[1][0] = 'modified'
original[2]['a'] = 'changed'

print(f"原始对象: {original}")
print(f"深拷贝: {deep_copy}")  # 深拷贝不受影响

不同数据类型的拷贝行为

1. 列表(List)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def demonstrate_list_copy():
    # 简单列表
    simple_list = [1, 2, 3]
    shallow = copy.copy(simple_list)
    deep = copy.deepcopy(simple_list)
    
    print("=== 简单列表 ===")
    print(f"原始: {simple_list}")
    print(f"浅拷贝: {shallow}")
    print(f"深拷贝: {deep}")
    
    # 嵌套列表
    nested_list = [1, [2, 3], [4, [5, 6]]]
    shallow = copy.copy(nested_list)
    deep = copy.deepcopy(nested_list)
    
    print("\n=== 嵌套列表 ===")
    print(f"原始: {nested_list}")
    print(f"浅拷贝: {shallow}")
    print(f"深拷贝: {deep}")
    
    # 修改嵌套元素
    nested_list[1][0] = 'X'
    print("\n=== 修改后 ===")
    print(f"原始: {nested_list}")
    print(f"浅拷贝: {shallow}")  # 受影响
    print(f"深拷贝: {deep}")     # 不受影响

demonstrate_list_copy()
2. 字典(Dictionary)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def demonstrate_dict_copy():
    # 简单字典
    simple_dict = {'a': 1, 'b': 2}
    shallow = copy.copy(simple_dict)
    deep = copy.deepcopy(simple_dict)
    
    print("=== 简单字典 ===")
    print(f"原始: {simple_dict}")
    print(f"浅拷贝: {shallow}")
    print(f"深拷贝: {deep}")
    
    # 嵌套字典
    nested_dict = {
        'a': 1,
        'b': {'x': 2, 'y': 3},
        'c': {'p': {'q': 4}}
    }
    shallow = copy.copy(nested_dict)
    deep = copy.deepcopy(nested_dict)
    
    print("\n=== 嵌套字典 ===")
    print(f"原始: {nested_dict}")
    print(f"浅拷贝: {shallow}")
    print(f"深拷贝: {deep}")
    
    # 修改嵌套元素
    nested_dict['b']['x'] = 'modified'
    print("\n=== 修改后 ===")
    print(f"原始: {nested_dict}")
    print(f"浅拷贝: {shallow}")  # 受影响
    print(f"深拷贝: {deep}")     # 不受影响

demonstrate_dict_copy()
3. 自定义对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person:
    def __init__(self, name, address):
        self.name = name
        self.address = address
    
    def __repr__(self):
        return f"Person(name='{self.name}', address={self.address})"

class Address:
    def __init__(self, street, city):
        self.street = street
        self.city = city
    
    def __repr__(self):
        return f"Address(street='{self.street}', city='{self.city}')"

def demonstrate_object_copy():
    # 创建对象
    addr = Address("123 Main St", "Boston")
    person = Person("John", addr)
    
    # 创建拷贝
    shallow = copy.copy(person)
    deep = copy.deepcopy(person)
    
    print("=== 原始状态 ===")
    print(f"原始: {person}")
    print(f"浅拷贝: {shallow}")
    print(f"深拷贝: {deep}")
    
    # 修改地址
    person.address.street = "456 Oak St"
    print("\n=== 修改后 ===")
    print(f"原始: {person}")
    print(f"浅拷贝: {shallow}")  # address被修改
    print(f"深拷贝: {deep}")     # 保持不变

demonstrate_object_copy()

特殊情况和注意事项

1. 循环引用
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def demonstrate_circular_reference():
    class Node:
        def __init__(self):
            self.data = None
            self.next = None
        
        def __repr__(self):
            return f"Node(data={self.data})"
    
    # 创建循环引用
    node1 = Node()
    node2 = Node()
    node1.data = 1
    node2.data = 2
    node1.next = node2
    node2.next = node1
    
    # 深拷贝可以正确处理循环引用
    copied = copy.deepcopy(node1)
    print(f"原始节点: {node1}")
    print(f"拷贝的节点: {copied}")
    print(f"是否是同一个对象: {node1 is copied}")

demonstrate_circular_reference()
2. 不可变对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def demonstrate_immutable_copy():
    # 数字
    num = 42
    num_copy = copy.copy(num)
    print(f"数字拷贝: {num is num_copy}")  # True
    
    # 字符串
    string = "Hello"
    string_copy = copy.copy(string)
    print(f"字符串拷贝: {string is string_copy}")  # True
    
    # 元组
    tuple_simple = (1, 2, 3)
    tuple_copy = copy.copy(tuple_simple)
    print(f"简单元组拷贝: {tuple_simple is tuple_copy}")  # True
    
    # 包含可变对象的元组
    nested_tuple = (1, [2, 3], 4)
    nested_copy = copy.deepcopy(nested_tuple)
    print(f"嵌套元组深拷贝: {nested_tuple is nested_copy}")  # False

demonstrate_immutable_copy()

性能考虑

1. 拷贝性能测试
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import timeit

def compare_copy_performance():
    # 准备测试数据
    simple_list = list(range(1000))
    nested_list = [list(range(10)) for _ in range(100)]
    
    # 测试不同拷贝方法的性能
    def test_assignment():
        new_list = simple_list
    
    def test_shallow_copy():
        new_list = copy.copy(simple_list)
    
    def test_deep_copy():
        new_list = copy.deepcopy(simple_list)
    
    # 执行测试
    assignment_time = timeit.timeit(test_assignment, number=10000)
    shallow_time = timeit.timeit(test_shallow_copy, number=10000)
    deep_time = timeit.timeit(test_deep_copy, number=10000)
    
    print(f"简单赋值时间: {assignment_time:.6f} 秒")
    print(f"浅拷贝时间: {shallow_time:.6f} 秒")
    print(f"深拷贝时间: {deep_time:.6f} 秒")

compare_copy_performance()

实际应用场景

1. 配置对象的复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Configuration:
    def __init__(self):
        self.settings = {
            'database': {
                'host': 'localhost',
                'port': 5432
            },
            'cache': {
                'enabled': True,
                'timeout': 300
            }
        }
    
    def create_test_config(self):
        # 创建配置的深拷贝用于测试
        test_config = copy.deepcopy(self)
        test_config.settings['database']['host'] = 'test-db'
        return test_config

# 使用示例
config = Configuration()
test_config = config.create_test_config()
print(f"原始配置: {config.settings}")
print(f"测试配置: {test_config.settings}")
2. 游戏状态保存
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class GameState:
    def __init__(self):
        self.player = {
            'health': 100,
            'inventory': ['sword', 'shield'],
            'position': {'x': 0, 'y': 0}
        }
        self.enemies = [
            {'type': 'goblin', 'health': 50},
            {'type': 'orc', 'health': 100}
        ]
    
    def save_checkpoint(self):
        # 创建游戏状态的深拷贝作为存档点
        return copy.deepcopy(self)

# 使用示例
game = GameState()
checkpoint = game.save_checkpoint()

# 修改当前游戏状态
game.player['health'] -= 30
game.player['inventory'].append('potion')

print("当前游戏状态:", game.player)
print("存档点状态:", checkpoint.player)

最佳实践

  1. 选择合适的拷贝方式:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def choose_copy_method(data):
    # 简单的不可变对象
    if isinstance(data, (int, float, str, bool)):
        return data
    
    # 只包含不可变对象的简单容器
    if isinstance(data, (list, dict)) and all(isinstance(x, (int, float, str, bool)) for x in data):
        return copy.copy(data)
    
    # 复杂的嵌套结构
    return copy.deepcopy(data)
  1. 优化深拷贝性能:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class OptimizedObject:
    def __init__(self):
        self.immutable_data = (1, 2, 3)
        self.mutable_data = [4, 5, 6]
    
    def __deepcopy__(self, memo):
        # 自定义深拷贝行为
        new_obj = OptimizedObject()
        memo[id(self)] = new_obj
        # 不可变数据直接引用
        new_obj.immutable_data = self.immutable_data
        # 可变数据需要深拷贝
        new_obj.mutable_data = copy.deepcopy(self.mutable_data, memo)
        return new_obj
  1. 处理特殊情况:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SpecialObject:
    def __init__(self):
        self.data = []
        self.no_copy = None  # 不需要拷贝的属性
    
    def __copy__(self):
        # 自定义浅拷贝行为
        obj = type(self)()
        obj.__dict__.update(self.__dict__)
        obj.data = self.data.copy()
        return obj
    
    def __deepcopy__(self, memo):
        # 自定义深拷贝行为
        obj = type(self)()
        memo[id(self)] = obj
        for k, v in self.__dict__.items():
            if k == 'no_copy':
                obj.__dict__[k] = v  # 直接引用
            else:
                obj.__dict__[k] = copy.deepcopy(v, memo)
        return obj

常见陷阱和解决方案

1. 循环引用处理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Node:
    def __init__(self, data):
        self.data = data
        self.references = []
    
    def add_reference(self, node):
        self.references.append(node)
    
    def __deepcopy__(self, memo):
        if id(self) in memo:
            return memo[id(self)]
        
        new_node = Node(copy.deepcopy(self.data, memo))
        memo[id(self)] = new_node
        new_node.references = copy.deepcopy(self.references, memo)
        return new_node
2. 资源处理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ResourceHandler:
    def __init__(self):
        self.resource = open('temp.txt', 'w')
        self.data = []
    
    def __copy__(self):
        # 创建新的资源句柄
        new_obj = type(self)()
        new_obj.data = self.data.copy()
        return new_obj
    
    def __del__(self):
        self.resource.close()
3. 性能优化
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class CacheAwareObject:
    def __init__(self):
        self.expensive_data = self._compute_expensive_data()
        self._cache = {}
    
    def _compute_expensive_data(self):
        # 假设这是一个耗时的计算
        return [i ** 2 for i in range(1000)]
    
    def __deepcopy__(self, memo):
        # 避免重新计算昂贵的数据
        new_obj = type(self)()
        memo[id(self)] = new_obj
        new_obj.expensive_data = self.expensive_data  # 直接共享不可变数据
        new_obj._cache = {}  # 创建新的缓存字典
        return new_obj

总结

  1. 拷贝类型的选择:
    • 对于简单的不可变对象,直接赋值即可
    • 对于只包含不可变对象的容器,使用浅拷贝
    • 对于包含可变对象的复杂结构,使用深拷贝
  2. 性能考虑:
    • 深拷贝比浅拷贝更耗时和内存
    • 可以通过自定义__copy__和__deepcopy__方法优化性能
    • 对于大型对象,考虑增量式拷贝或懒拷贝
  3. 最佳实践:
    • 明确对象的可变性和依赖关系
    • 合理使用自定义拷贝方法
    • 注意处理特殊情况(如循环引用)
    • 在性能关键的场景中谨慎使用深拷贝

通过理解Python的拷贝机制,我们可以更好地管理对象的状态和依赖关系,编写更可靠的代码。记住,选择合适的拷贝方式取决于具体的使用场景和需求。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
由浅入深,66条JavaScript面试知识点
来源:https://juejin.im/post/5ef8377f6fb9a07e693a6061
zz_jesse
2020/07/07
1.1K0
由浅入深,66条JavaScript面试知识点
分享 9个超级实用的 Javascript 技巧
在实际的开发工作过程中,我积累了一些常用的、超级有用的Javascript技巧和代码片段,包括其他大神编译的JS使用技巧。
前端达人
2023/08/31
2030
分享 9个超级实用的 Javascript 技巧
前端进阶|由浅入深的理解函数柯里化
柯里化(Currying)和反柯里化(Uncurrying)在JavaScript中总感觉属于一种不温不火的存在,甚至有些开发者在提起柯里化和反柯里化时,竟然会有点生疏不懂。其实不然,对于它们的概念可能在日常开发中不太提到,但是它们的思想和用法,却在前端开发中经常借鉴和使用,它可以帮助我们写出更加优雅、灵活的代码。本文会首先介绍柯里化的概念、实现原理和应用场景,希望对大家能有所帮助!
anyup
2023/11/27
5750
前端进阶|由浅入深的理解函数柯里化
提升开发技能:10个高级的JavaScript技巧
在这个快速发展的数字时代,JavaScript作为一种广泛应用的编程语言,其重要性愈发凸显。为了在竞争激烈的开发领域中保持竞争力,不断提升自己的技能是至关重要的。本文小编将您介绍10个高级的JavaScript技巧,旨在帮助开发者们在编码过程中更加高效和灵活。
葡萄城控件
2023/11/28
2840
详解JavaScript中闭包(Closure)概念
闭包(Closure)是 JavaScript 中一个非常重要的概念,它允许函数访问其词法作用域(lexical scope)中的变量,即使这个函数在其词法作用域之外执行。理解闭包有助于编写更高效、模块化的代码,并且在处理异步操作、回调函数和数据封装时非常有用。
jack.yang
2025/04/05
990
现在就可以使用的 20 个 JavaScript 技巧和窍门
今天探讨 20 种 JavaScript 技巧和窍门,每种技巧和窍门都有通俗易懂的示例。让我们一起来提升你的 JavaScript 技能吧!
前端小智@大迁世界
2024/02/12
1670
聊一聊如何像大神一般玩转 JavaScript 的高级用法
众所周知,JavaScript是一种非常流行的编程语言,它已经成为了网页开发的必备技能。但是,在我们从事JavaScript编程的时候,我们却没有完全发掘和利用它的全部潜力。在本文中,我们将分享一些高级的JavaScript技巧,希望帮助掘友们更好地理解和掌握JavaScript编程。
前端达人
2023/09/21
2570
聊一聊如何像大神一般玩转 JavaScript 的高级用法
JavaScript高级技巧
以上代码要返回true,value必须是一个数组,而且还必须与Array构造函数在同个全局作用域中。(Array是window的属性)如果value是在另外一个iframe中定义的数组,上述代码则返回false。 注意:BOM的核心对象时window,它表示浏览器的一个实例。在浏览器中,window对象有双重角色,它既是通过JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的global对象。 解决上述问题: Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串。
奋飛
2019/08/15
1.1K0
JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this
所以,JavaScript是ArkTS的基础,本文就来介绍一下JavaScript执行机制的一些核心概念。
陆业聪
2024/07/23
2200
JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this
4 个关于JavaScript 中闭包的有用技巧
英文 | https://javascript.plainenglish.io/4-tricks-and-tips-about-closure-in-javascript-you-should-know-a7fe6aeaa767
前端老道
2023/10/09
1850
4 个关于JavaScript 中闭包的有用技巧
前端速记
日常记录一些 js/css 相对实用的小笔记,本笔记保持长期更新,如有错误或更好的方案留言反馈
2Broear
2024/03/12
2100
前端速记
2021JavaScript面试题(最新)不定时更新(2021.11.6更新)
js 一共有六种基本数据类型,分别是 Undefined、Null、Boolean、Number、String,还有在 ES6 中新增的 Symbol 类型。 Symbol 代表创建后独一无二且不可变的数据类型,它的出现我认为主要是为了解决可能出现的全局变量冲突的问题。
全栈程序员站长
2022/09/07
2.7K0
由浅入深,66条JavaScript面试知识点
作者:Jake Zhang https://juejin.cn/post/6844904200917221389
用户4456933
2021/06/01
7440
由浅入深,66条JavaScript面试知识点
《The Joy of Javascript》- 5 - Data
需要注意的是 for await……of 需要一个对象拥有一个 function-valued symbol property Symbol.asyncIterator, 因此可以如此设计一个对象用于 for await……of
szhshp
2022/09/21
6940
美团前端二面面试题_2023-02-28
JavaScript中Number.MAX_SAFE_INTEGER表示最⼤安全数字,计算结果是9007199254740991,即在这个数范围内不会出现精度丢失(⼩数除外)。但是⼀旦超过这个范围,js就会出现计算不准确的情况,这在⼤数计算的时候不得不依靠⼀些第三⽅库进⾏解决,因此官⽅提出了BigInt来解决此问题。
用户10357900
2023/02/28
4880
无敌秘籍之 — JavaScript手写代码
用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的reviver函数用以在返回之前对所得到的对象执行变换(操作)。
小生方勤
2019/07/15
4380
无敌秘籍之 — JavaScript手写代码
面了十多家,总结出20道JavaScript 必考的面试题!
面临毕业季,相信有很多朋友正在进行找工作,背面试题;今天就分享给大家20道JavaScript必会的问题
程序员老鱼
2023/09/07
2480
面了十多家,总结出20道JavaScript 必考的面试题!
2022高频前端面试题合集之JavaScript篇(上)
解析:该题主要考察就是对 js 中的继承是否了解,以及常见的继承的形式有哪些。最常用的继承就是「组合继承」(伪经典继承)和圣杯模式继承。下面附上 js 中这两种继承模式的详细解析。
程序员法医
2022/12/20
1.2K0
2022高频前端面试题合集之JavaScript篇(上)
提升您的 Web 开发游戏:每个开发人员都应该掌握的 12 个 JavaScript 功能
JavaScript 是一种多才多艺的编程语言,在现代Web开发中发挥着至关重要的作用。无论您是经验丰富的开发人员还是初学者,掌握 JavaScript 的某些特性可以显著提升您的编码技能,帮助您构建更高效、可维护的Web应用程序。在这篇文章中,我们将探讨每个Web开发人员都应该熟悉的12个 JavaScript 特性。
泽霖
2023/12/08
2981
《现代Javascript高级教程》Javascript执行上下文与闭包
JavaScript中的闭包源于计算机科学中的一种理论概念,称为“λ演算”(Lambda Calculus)。λ演算是计算机科学的基础之一,1930年由Alonzo Church提出,它是一种用于描述计算过程的数学抽象模型,也是函数式编程语言的基础。
linwu
2023/07/27
2100
推荐阅读
相关推荐
由浅入深,66条JavaScript面试知识点
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验