Python標準庫筆記(7) — copy模塊

copy-對象拷貝模塊;提供了淺拷貝和深拷貝複製對象的功能, 分別對應模塊中的兩個函數 copy()deepcopy()python

1.淺拷貝(Shallow Copies)

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 中引用的是同一個對象。app

2.深拷貝(Deep Copies)

deepcopy() 建立的 深拷貝 是一個新的容器,它包含了對原始對象的內容的拷貝。深拷貝徹底拷貝了父對象及其子對象。即建立一個新的組合對象,同時遞歸地拷貝全部子對象,新的組合對象與原對象沒有任何關聯。雖然實際上會共享不可變的子對象,但不影響它們的相互獨立性。ide

將上面代碼換成 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 實例再也不是同一個的對象引用,而是從新複製了一份, 可是當兩個對象被比較時,它們的值仍然是相等的。code

3.自定義拷貝行爲

能夠經過自定義 __copy__()__deepcopy__() 方法來改變默認的拷貝行爲。orm

  • __copy()__ 是一個無參數方法,它返回一個淺拷貝對象;對象

  • __deepcopy()__ 接受一個備忘(memo)字典參數,返回一個深拷貝對象。須要進行深拷貝的成員屬性都應該傳遞給 copy.deepcopy() ,以及memo字典,以控制遞歸。(下面例子將解釋memo字典)。blog

下面的示例演示如何調用這些方法:遞歸

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字典用於跟蹤已經拷貝的值,以免無限遞歸。

4.深拷貝中的遞歸

爲了不拷貝時有遞歸數據結構的問題, 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)

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字典中重用現有的值,而不是建立一個新對象。

轉載請註明來源: http://www.spiderpy.cn/blog/detail/38

相關文章
相關標籤/搜索