2017-02-21 47 views
7

我班的一個對象有一個列表作爲它的屬性。也就是說,如果我想要我的對象的非遞歸深層副本,我應該覆蓋Python中的複製或深層複製嗎?

class T(object): 
    def __init__(self, x, y): 
     self.arr = [x, y] 

當這個對象被複制,我想一個單獨的列表arr,但列表(例如xy)的內容的淺表副本。因此,我決定實現我自己的複製方法,它將重新創建列表,但不會創建其中的項目。但是,我應該叫這個__copy__()__deepcopy__()?根據Python的語義,哪一個是我所做的正確名稱?

我的猜測是__copy__()。如果我撥打deepcopy(),我希望克隆與原始克隆完全分離。然而,documentation說:

一個深拷貝構造一個新的複合對象,然後,遞歸, 插入副本到它在原來發現的對象。

這是說混亂「插入副本進去」,而不是「插入深層副本進去」,尤其是重點。

+0

「深」的概念來自遞歸部分,否則說「深層副本構造一個新的複合對象,然後插入深層副本」將是一個遞歸描述。 –

回答

5

您在這裏實現的正確魔術方法是__copy__

您描述的行爲比複製的默認行爲更深(即對於沒有打算實施__copy__的對象),但其深度不足以稱爲深度複製。因此,您應該實施__copy__以獲得所需的行爲。

不要讓思維的錯誤,只是分配的另一個名字讓「複製」:

t1 = T('google.com', 123) 
t2 = t1 # this does *not* use __copy__ 

這只是結合了另一個名稱相同的實例。相反,__copy__方法是通過一個函數來鉤掛到:

import copy 
t2 = copy.copy(t1) 
+0

謝謝!我認爲是一樣的,但當我閱讀Python文檔時感到疑惑。爲什麼Python不會像'str'和'repr'那樣''複製'內建'? – user31039

+0

因爲它沒有太多的用例。 Python開發人員通常使用避免副作用的風格,因此他們並不真正關心對象所處的內存位置。 – wim

+0

@ user31039因爲大多數內置的內容都可以很容易地複製。例如,一個實例可以傳遞給構造函數('dict({'a':1})'做一個副本)或者有一個顯式的'copy'方法:'{1,2,3} .copy()'或者甚至有一個切片語法來創建一個副本:'[1,2,3] [:]'。對於使用builtins的普通python程序,你只需要不需要copy模塊。廣告:我在我的回答中也提到了其中的一些觀點。 :) – MSeifert

1

我想改寫__copy__是一個好主意,因爲用非常詳細的其他答案解釋。另一種解決方案可能是寫一個明確的複製方法是對所謂絕對明確:

class T(object): 
    def __init__(self, x, y): 
     self.arr = [x, y] 

    def copy(self): 
     return T(*self.arr) 

當任何未來讀者看到t.copy()他於是馬上知道,一個自定義的複製方法已經實施。當然缺點是它可能不適合使用copy.copy(t)的第三方庫。

+0

所以你對這個問題的答案是「我應該覆蓋複製還是深度複製?」從標題是...既不,並忘記'copy.copy'所有在一起? –

+0

這只是一個替代的想法。我認爲它有一些優勢,但我也完全意識到這些消極影響。 –

+2

沒問題,只是如果'__copy__'被定義了,當你執行'copy.copy(t)'時會調用它。在我看來,你並沒有真正提供解決問題的方法。 (我認爲downvoter是出於這個原因) –

3

它實際上取決於您的班級所需的行爲,決定要重寫的內容(__copy____deepcopy__)。

一般copy.deepcopy作品大多正常,它只是複製一切(recursivly),所以你只需要重寫,如果有一些屬性是不得複製(永遠!)。

另一方面,只有當用戶(包括您自己)不希望更改傳播到複製的實例時,才應該定義__copy__。例如,如果您僅包裝可變類型(如list)或使用可變類型作爲實現細節。

然後還有一種情況是,要複製的最小屬性集沒有明確定義。在這種情況下,我也會覆蓋__copy__,但也許會在那裏提高TypeError,並且可能包含一個(或多個)專用公衆copy方法。

然而在我看來,arr算作實現細節,因此我會覆蓋__copy__

class T(object): 
    def __init__(self, x, y): 
     self.arr = [x, y] 

    def __copy__(self): 
     new = self.__class__(*self.arr) 
     # ... maybe other stuff 
     return new 

只是爲了顯示它按預期工作:

from copy import copy, deepcopy 

x = T([2], [3]) 
y = copy(x) 
x.arr is y.arr  # False 
x.arr[0] is y.arr[0] # True 
x.arr[1] is y.arr[1] # True 

x = T([2], [3]) 
y = deepcopy(x) 
x.arr is y.arr  # False 
x.arr[0] is y.arr[0] # False 
x.arr[1] is y.arr[1] # False 

只是快速的音符關於期望:

用戶一般期望t您可以將實例傳遞給構造函數以創建最小副本(與__copy__類似或相同)。例如:

lst1 = [1,2,3,4] 
lst2 = list(lst1) 
lst1 is lst2  # False 

一些Python類型具有顯式copy方法,即(如果存在)應該做相同__copy__。這將允許在參數明確地傳遞(不過我還沒有看到行動尚未這個):

lst3 = lst1.copy() # python 3.x only (probably) 
lst3 is lst1  # False 

如果你的類應該被其他人使用你可能需要考慮這些問題,但是如果你只是想讓你的課程與copy.copy一起工作,然後覆蓋__copy__

相關問題