知行编程网知行编程网  2022-01-07 17:53 知行编程网 隐藏边栏 |   抢沙发  10 
文章评分 0 次,平均分 0.0

1.浅拷贝(Shallow Copies)

copy() 创建的 浅拷贝 是一个新的容器,它包含了对原始对象的内容的引用。也就是说仅拷贝父对象,不会拷贝对象的内部的子对象。即浅复制只复制对象本身,没有复制该对象所引用的对象。比如,当创建一个列表对象的浅拷贝时,将构造一个新的列表,并将原始对象的元素添加给它。

  1. import copy
  2.  
  3. class MyClass:
  4.  
  5. def __init__(self, name):
  6. self.name = name
  7.  
  8. def __eq__(self, other):
  9. return self.name == other.name
  10.  
  11. def __gt__(self, other):
  12. return self.name > other.name
  13.  
  14. a = MyClass('a')
  15. my_list = [a]
  16. dup = copy.copy(my_list)
  17.  
  18. print(' my_list:', my_list)
  19. print(' dup:', dup)
  20. print(' dup is my_list:', (dup is my_list))
  21. print(' dup == my_list:', (dup == my_list))
  22. print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
  23. print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))
  1. my_list: [<__main__.MyClass object at 0x0000026DFF98D128>]
  2. dup: [<__main__.MyClass object at 0x0000026DFF98D128>]
  3. dup is my_list: False
  4. dup == my_list: True
  5. dup[0] is my_list[0]: True
  6. dup[0] == my_list[0]: True

上面的浅拷贝实例中,dup 是由 my_list 拷贝而来, 但是 MyClass 实例不会拷贝,所以 dup 列表与 my_list 中引用的是同一个对象。

2.深拷贝(Deep Copies)

deepcopy() 创建的 深拷贝 是一个新的容器,它包含了对原始对象的内容的拷贝。深拷贝完全拷贝了父对象及其子对象。即创建一个新的组合对象,同时递归地拷贝所有子对象,新的组合对象与原对象没有任何关联。虽然实际上会共享不可变的子对象,但不影响它们的相互独立性。

将上面代码换成 deepcopy(),将会发现其中不同:

  1. import copy
  2.  
  3. class MyClass:
  4.  
  5. def __init__(self, name):
  6. self.name = name
  7.  
  8. def __eq__(self, other):
  9. return self.name == other.name
  10.  
  11. def __gt__(self, other):
  12. return self.name > other.name
  13.  
  14. a = MyClass('a')
  15. my_list = [a]
  16. dup = copy.deepcopy(my_list)
  17.  
  18. print(' my_list:', my_list)
  19. print(' dup:', dup)
  20. print(' dup is my_list:', (dup is my_list))
  21. print(' dup == my_list:', (dup == my_list))
  22. print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
  23. print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))
  1. my_list: [<__main__.MyClass object at 0x000002442E47D128>]
  2. dup: [<__main__.MyClass object at 0x00000244352EF208>]
  3. dup is my_list: False
  4. dup == my_list: True
  5. dup[0] is my_list[0]: False
  6. dup[0] == my_list[0]: True

列表中的 MyClass 实例不再是同一个的对象引用,而是重新复制了一份, 但是当两个对象被比较时,它们的值仍然是相等的。

3.自定义拷贝行为

可以通过自定义 __copy__() 和 __deepcopy__() 方法来改变默认的拷贝行为。

  • __copy()__ 是一个无参数方法,它返回一个浅拷贝对象;

  • __deepcopy()__ 接受一个备忘(memo)字典参数,返回一个深拷贝对象。需要进行深拷贝的成员属性都应该传递给 copy.deepcopy() ,以及memo字典,以控制递归。(下面例子将解释memo字典)。

下面的示例演示如何调用这些方法:

  1. import copy
  2.  
  3. class MyClass:
  4.  
  5. def __init__(self, name):
  6. self.name = name
  7.  
  8. def __eq__(self, other):
  9. return self.name == other.name
  10.  
  11. def __gt__(self, other):
  12. return self.name > other.name
  13.  
  14. def __copy__(self):
  15. print('__copy__()')
  16. return MyClass(self.name)
  17.  
  18. def __deepcopy__(self, memo):
  19. print('__deepcopy__({})'.format(memo))
  20. return MyClass(copy.deepcopy(self.name, memo))
  21.  
  22. a = MyClass('a')
  23.  
  24. sc = copy.copy(a)
  25. dc = copy.deepcopy(a)
  1. __copy__()
  2. __deepcopy__({})

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

4.深拷贝中的递归

为了避免拷贝时有递归数据结构的问题, deepcopy()`` 使用一个字典来跟踪已经拷贝的对象。这个字典被传递给deepcopy()` 方法进行检查。

下面示例展示了一个相互关联的数据结构(有向图),如何通过实现 __deepcopy__() 方法来防止递归。

  1. import copy
  2.  
  3. class Graph:
  4.  
  5. def __init__(self, name, connections):
  6. self.name = name
  7. self.connections = connections
  8.  
  9. def add_connection(self, other):
  10. self.connections.append(other)
  11.  
  12. def __repr__(self):
  13. return 'Graph(name={}, id={})'.format(
  14. self.name, id(self))
  15.  
  16. def __deepcopy__(self, memo):
  17. print('nCalling __deepcopy__ for {!r}'.format(self))
  18. if self in memo:
  19. existing = memo.get(self)
  20. print(' Already copied to {!r}'.format(existing))
  21. return existing
  22. print(' Memo dictionary:')
  23. if memo:
  24. for k, v in memo.items():
  25. print(' {}: {}'.format(k, v))
  26. else:
  27. print(' (empty)')
  28. dup = Graph(copy.deepcopy(self.name, memo), [])
  29. print(' Copying to new object {}'.format(dup))
  30. memo[self] = dup
  31. for c in self.connections:
  32. dup.add_connection(copy.deepcopy(c, memo))
  33. return dup
  34.  
  35. root = Graph('root', [])
  36. a = Graph('a', [root])
  37. b = Graph('b', [a, root])
  38. root.add_connection(a)
  39. root.add_connection(b)
  40.  
  41. dup = copy.deepcopy(root)

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

__deepcopy__() 方法打印了它的调用信息,并根据需要管理memo字典内容。它不会复制整个连接列表,而是创建一个新的列表,并将单个连接的副本添加进去。确保在每个新节点被复制时更新memo字典,并且避免递归或重复拷贝节点。与以前一样,该方法在完成时返回拷贝的对象。

Python模块学习 ---- copy模块

  1. Calling __deepcopy__ for Graph(name=root, id=2115579269360)
  2. Memo dictionary:
  3. (empty)
  4. Copying to new object Graph(name=root, id=2115695211072)
  5.  
  6. Calling __deepcopy__ for Graph(name=a, id=2115695210904)
  7. Memo dictionary:
  8. Graph(name=root, id=2115579269360): Graph(name=root, id=2115695211072)
  9. Copying to new object Graph(name=a, id=2115695211184)
  10.  
  11. Calling __deepcopy__ for Graph(name=root, id=2115579269360)
  12. Already copied to Graph(name=root, id=2115695211072)
  13.  
  14. Calling __deepcopy__ for Graph(name=b, id=2115695210960)
  15. Memo dictionary:
  16. Graph(name=root, id=2115579269360): Graph(name=root, id=2115695211072)
  17. Graph(name=a, id=2115695210904): Graph(name=a, id=2115695211184)
  18. 2115579269360: Graph(name=root, id=2115695211072)
  19. 2115695219408: [Graph(name=root, id=2115579269360), Graph(name=a, id=2115695210904)]
  20. 2115695210904: Graph(name=a, id=2115695211184)
  21. Copying to new object Graph(name=b, id=2115695211240)

第二次遇到根节点时,如果一个节点被已拷贝时, __deepcopy__() 检测递归,并从memo字典中重用现有的值,而不是创建一个新对象。

 

Python模块学习系列:
Python模块学习 ---- re正则表达式
Python模块学习 ---- hashlib模块
Python模块学习 ---- random模块

Python模块学习 ---- smtplib模块

Python模块学习 ---- glob模块
Python模块学习 ---- logging模块

Python模块学习 ---- zipfile模块

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

知行编程网
知行编程网 关注:1    粉丝:1
这个人很懒,什么都没写

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享