每周一个 Python 模块 | copy

简介: copy 模块包括两个功能,copy() 和 deepcopy(),用于复制现有对象。

浅拷贝


copy() 创建的浅表副本是一个新容器,是对原始对象内容的引用。


import copy
import functools
@functools.total_ordering
class MyClass:
    def __init__(self, name):
        self.name = name
    def __eq__(self, other):
        return self.name == other.name
    def __gt__(self, other):
        return self.name > other.name
a = MyClass('a')
my_list = [a]
dup = copy.copy(my_list)
print('             my_list:', my_list)
print('                 dup:', dup)
print('      dup is my_list:', (dup is my_list))
print('      dup == my_list:', (dup == my_list))
print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))
# output
#              my_list: [<__main__.MyClass object at 0x101f9c160>]
#                  dup: [<__main__.MyClass object at 0x101f9c160>]
#       dup is my_list: False
#       dup == my_list: True
# dup[0] is my_list[0]: True
# dup[0] == my_list[0]: True
复制代码


对于浅拷贝,MyClass 实例并不复制,因此dupmy_list 引用的是同一个对象。


深拷贝


将调用替换为 deepcopy() 会使输出明显不同。


import copy
import functools
@functools.total_ordering
class MyClass:
    def __init__(self, name):
        self.name = name
    def __eq__(self, other):
        return self.name == other.name
    def __gt__(self, other):
        return self.name > other.name
a = MyClass('a')
my_list = [a]
dup = copy.deepcopy(my_list)
print('             my_list:', my_list)
print('                 dup:', dup)
print('      dup is my_list:', (dup is my_list))
print('      dup == my_list:', (dup == my_list))
print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))
# output
#              my_list: [<__main__.MyClass object at 0x101e9c160>]
#                  dup: [<__main__.MyClass object at 0x1044e1f98>]
#       dup is my_list: False
#       dup == my_list: True
# dup[0] is my_list[0]: False
# dup[0] == my_list[0]: True
复制代码


列表的第一个元素不再是相同的对象引用,但是当比较两个对象时,它们仍然是相等的。


自定义复制行为


可以使用  __copy__()__deepcopy__() 方法来自定义复制行为。

  • __copy__() 不需要参数,返回该对象的浅拷贝副本。
  • __deepcopy__()使用 memo 字典调用,并返回该对象的深拷贝对象。任何需要深度复制的成员属性,都应与 memo 字典一起传递给 copy.deepcopy()

以下示例说明了如何调用方法。


import copy
import functools
@functools.total_ordering
class MyClass:
    def __init__(self, name):
        self.name = name
    def __eq__(self, other):
        return self.name == other.name
    def __gt__(self, other):
        return self.name > other.name
    def __copy__(self):
        print('__copy__()')
        return MyClass(self.name)
    def __deepcopy__(self, memo):
        print('__deepcopy__({})'.format(memo))
        return MyClass(copy.deepcopy(self.name, memo))
a = MyClass('a')
sc = copy.copy(a)
dc = copy.deepcopy(a)
# output
# __copy__()
# __deepcopy__({})
复制代码


memo 字典用于跟踪已经复制的值,以避免无限递归。


深度复制中的递归


为避免重复递归数据结构的问题,deepcopy() 使用字典来跟踪已复制的对象。这个字典被传递给__deepcopy__() 方法,因此可以在这里检查重复递归问题。

下一个示例显示了互连数据结构(如有向图)如何通过实现__deepcopy__()方法来防止递归。


import copy
class Graph:
    def __init__(self, name, connections):
        self.name = name
        self.connections = connections
    def add_connection(self, other):
        self.connections.append(other)
    def __repr__(self):
        return 'Graph(name={}, id={})'.format(
            self.name, id(self))
    def __deepcopy__(self, memo):
        print('\nCalling __deepcopy__ for {!r}'.format(self))
        if self in memo:
            existing = memo.get(self)
            print('  Already copied to {!r}'.format(existing))
            return existing
        print('  Memo dictionary:')
        if memo:
            for k, v in memo.items():
                print('    {}: {}'.format(k, v))
        else:
            print('    (empty)')
        dup = Graph(copy.deepcopy(self.name, memo), [])
        print('  Copying to new object {}'.format(dup))
        memo[self] = dup
        for c in self.connections:
            dup.add_connection(copy.deepcopy(c, memo))
        return dup
root = Graph('root', [])
a = Graph('a', [root])
b = Graph('b', [a, root])
root.add_connection(a)
root.add_connection(b)
dup = copy.deepcopy(root)
# output
# Calling __deepcopy__ for Graph(name=root, id=4326183824)
#   Memo dictionary:
#     (empty)
#   Copying to new object Graph(name=root, id=4367233208)
# 
# Calling __deepcopy__ for Graph(name=a, id=4326186344)
#   Memo dictionary:
#     Graph(name=root, id=4326183824): Graph(name=root, id=4367233208)
#   Copying to new object Graph(name=a, id=4367234720)
# 
# Calling __deepcopy__ for Graph(name=root, id=4326183824)
#   Already copied to Graph(name=root, id=4367233208)
# 
# Calling __deepcopy__ for Graph(name=b, id=4326183880)
#   Memo dictionary:
#     Graph(name=root, id=4326183824): Graph(name=root, id=4367233208)
#     Graph(name=a, id=4326186344): Graph(name=a, id=4367234720)
#     4326183824: Graph(name=root, id=4367233208)
#     4367217936: [Graph(name=root, id=4326183824), Graph(name=a, id=4326186344)]
#     4326186344: Graph(name=a, id=4367234720)
#   Copying to new object Graph(name=b, id=4367235000)
复制代码


Graph 类包括几个基本的有向图的方法。可以使用名称和与其连接的现有节点列表初始化实例。add_connection() 方法用于设置双向连接。它也被深拷贝操作符使用。

__deepcopy__()方法打印消息以显示其调用方式,并根据需要管理备忘录字典内容。它不是复制整个连接列表,而是创建一个新列表,并将各个连接的副本添加进去。这确保了备忘录字典在每个新节点被复制时更新,并且它避免了递归问题或节点的额外副本。和以前一样,该方法在完成后返回复制的对象。


具有循环的对象图的深层复制

图中显示的图形包括几个周期,但使用备注字典处理递归可防止遍历导致堆栈溢出错误。

第二次根遇到一个节点,而这个节点被复制,__deepcopy__()检测该递归和重用来自备忘录字典现有值而不是创建新的对象。

目录
相关文章
|
27天前
|
开发者 Python
如何在Python中管理模块和包的依赖关系?
在实际开发中,通常会结合多种方法来管理模块和包的依赖关系,以确保项目的顺利进行和可维护性。同时,要及时更新和解决依赖冲突等问题,以保证代码的稳定性和可靠性
44 4
|
7天前
|
Python
Python Internet 模块
Python Internet 模块。
102 74
|
25天前
|
算法 数据安全/隐私保护 开发者
马特赛特旋转算法:Python的随机模块背后的力量
马特赛特旋转算法是Python `random`模块的核心,由松本真和西村拓士于1997年提出。它基于线性反馈移位寄存器,具有超长周期和高维均匀性,适用于模拟、密码学等领域。Python中通过设置种子值初始化状态数组,经状态更新和输出提取生成随机数,代码简单高效。
104 63
|
27天前
|
测试技术 Python
手动解决Python模块和包依赖冲突的具体步骤是什么?
需要注意的是,手动解决依赖冲突可能需要一定的时间和经验,并且需要谨慎操作,避免引入新的问题。在实际操作中,还可以结合使用其他方法,如虚拟环境等,来更好地管理和解决依赖冲突😉。
|
27天前
|
持续交付 Python
如何在Python中自动解决模块和包的依赖冲突?
完全自动解决所有依赖冲突可能并不总是可行,特别是在复杂的项目中。有时候仍然需要人工干预和判断。自动解决的方法主要是提供辅助和便捷,但不能完全替代人工的分析和决策😉。
|
1月前
|
JSON Linux 数据格式
Python模块:从入门到精通,只需一篇文章!
Python中的模块是将相关代码组织在一起的单元,便于重用和维护。模块可以是Python文件或C/C++扩展,Python标准库中包含大量模块,如os、sys、time等,用于执行各种任务。定义模块只需创建.py文件并编写代码,导入模块使用import语句。此外,Python还支持自定义模块和包,以及虚拟环境来管理项目依赖。
Python模块:从入门到精通,只需一篇文章!
|
27天前
|
Python
Python的模块和包
总之,模块和包是 Python 编程中非常重要的概念,掌握它们可以帮助我们更好地组织和管理代码,提高开发效率和代码质量
38 5
|
27天前
|
数据可视化 Python
如何在Python中解决模块和包的依赖冲突?
解决模块和包的依赖冲突需要综合运用多种方法,并且需要团队成员的共同努力和协作。通过合理的管理和解决冲突,可以提高项目的稳定性和可扩展性
|
1月前
|
JavaScript 前端开发 Python
python中的OS模块的基本使用
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。博客分享前端技术及全栈开发经验,持续更新中,期待您的关注和支持!🎉🎉🎉
35 0
|
1月前
|
JavaScript 前端开发 Python
python中的platform模块的基本使用
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。博客分享前端技术,助你成长。关注我,持续更新中!🎉🎉🎉
25 0