copy-對象拷貝模塊;提供了淺拷貝和深拷貝復制對象的功能, 分別對應模塊中的兩個函數 copy() 和 deepcopy()。
copy() 創建的 淺拷貝 是一個新的容器,它包含了對原始對象的內容的引用。也就是說僅拷貝父對象,不會拷貝對象的內部的子對象。即淺復制只復制對象本身,沒有復制該對象所引用的對象。比如,當創建一個列表對象的淺拷貝時,將構造一個新的列表,並將原始對象的元素添加給它。
import copy
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]))
my_list: [<__main__.MyClass object at 0x0000026DFF98D128>]
dup: [<__main__.MyClass object at 0x0000026DFF98D128>]
dup is my_list: False
dup == my_list: True
dup[0] is my_list[0]: True
dup[0] == my_list[0]: True
上面的淺拷貝實例中,dup 是由 my_list 拷貝而來, 但是 MyClass 實例不會拷貝,所以 dup 列表與 my_list 中引用的是同一個對象。
deepcopy() 創建的 深拷貝 是一個新的容器,它包含了對原始對象的內容的拷貝。深拷貝完全拷貝了父對象及其子對象。即創建一個新的組合對象,同時遞歸地拷貝所有子對象,新的組合對象與原對象沒有任何關聯。雖然實際上會共享不可變的子對象,但不影響它們的相互獨立性。
將上面代碼換成 deepcopy(),將會發現其中不同:
import copy
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]))
my_list: [<__main__.MyClass object at 0x000002442E47D128>]
dup: [<__main__.MyClass object at 0x00000244352EF208>]
dup is my_list: False
dup == my_list: True
dup[0] is my_list[0]: False
dup[0] == my_list[0]: True
列表中的 MyClass 實例不再是同一個的對象引用,而是重新復制了一份, 但是當兩個對象被比較時,它們的值仍然是相等的。
可以通過自定義__copy__()
和 __deepcopy__()
方法來改變默認的拷貝行為。
__copy()__
是一個無參數方法,它返回一個淺拷貝對象;
__deepcopy()__
接受一個備忘(memo)字典參數,返回一個深拷貝對象。需要進行深拷貝的成員屬性都應該傳遞給 copy.deepcopy() ,以及memo字典,以控制遞歸。(下面例子將解釋memo字典)。
下面的示例演示如何調用這些方法:
import copy
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)
__copy__()
__deepcopy__({
})
memo字典用於跟蹤已經拷貝的值,以避免無限遞歸。
為了避免拷貝時有遞歸數據結構的問題, deepcopy()使用一個字典來跟蹤已經拷貝的對象。這個字典被傳遞給 deepcopy() 方法進行檢查。
下面示例展示了一個相互關聯的數據結構(有向圖),如何通過實現 __deepcopy__()
方法來防止遞歸。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:711312441 尋找有志同道合的小伙伴,互幫互助,群裡還有不錯的視頻學習教程和PDF電子書! '''
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)
Graph 類包括一些基本的有向圖方法。可以用一個名稱和它所連接的現有節點的列表來初始化一個實例。 add_connection() 方法用於設置雙向連接。它也被深拷貝操作符使用。
__deepcopy__()
方法打印了它的調用信息,並根據需要管理memo字典內容。它不會復制整個連接列表,而是創建一個新的列表,並將單個連接的副本添加進去。確保在每個新節點被復制時更新memo字典,並且避免遞歸或重復拷貝節點。與以前一樣,該方法在完成時返回拷貝的對象。
Calling __deepcopy__ for Graph(name=root, id=2115579269360)
Memo dictionary:
(empty)
Copying to new object Graph(name=root, id=2115695211072)
Calling __deepcopy__ for Graph(name=a, id=2115695210904)
Memo dictionary:
Graph(name=root, id=2115579269360): Graph(name=root, id=2115695211072)
Copying to new object Graph(name=a, id=2115695211184)
Calling __deepcopy__ for Graph(name=root, id=2115579269360)
Already copied to Graph(name=root, id=2115695211072)
Calling __deepcopy__ for Graph(name=b, id=2115695210960)
Memo dictionary:
Graph(name=root, id=2115579269360): Graph(name=root, id=2115695211072)
Graph(name=a, id=2115695210904): Graph(name=a, id=2115695211184)
2115579269360: Graph(name=root, id=2115695211072)
2115695219408: [Graph(name=root, id=2115579269360), Graph(name=a, id=2115695210904)]
2115695210904: Graph(name=a, id=2115695211184)
Copying to new object Graph(name=b, id=2115695211240)
第二次遇到根節點時,如果一個節點被已拷貝時, __deepcopy__()
檢測遞歸,並從memo字典中重用現有的值,而不是創建一個新對象。