2017-02-21 3 views
7

私のクラスのオブジェクトはその属性としてリストを持っています。つまり、このオブジェクトがコピーされると、私は別のリストarrが、リストの内容の簡易コピーを(例えばxy)したい、オブジェクトの非再帰的ディープコピーが必要な場合は、Pythonでコピーまたはディープコピーを上書きする必要がありますか?

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

です。したがって私は自分のコピーメソッドを実装することにしました。これはリストを再作成しますが、その中のアイテムは再作成しません。しかし、私はこれを__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は 'copy'を' str'や 'repr'のような組み込み関数にしませんか? – user31039

+0

これには多くのユースケースがないためです。 Pythonの開発者は、一般に、副作用を避けるスタイルで動作するので、オブジェクトのメモリ内のどこに実際に気にしません。 – wim

+0

@ user31039ほとんどのビルトインが簡単にコピーできるためです。たとえば、コンストラクタ( 'dict({'a':1})'にコピーを渡すか、 '{1,2,3} .copy()'または'[1,2,3] [:]'というコピーを作成するスライス構文もあります。組み込み関数を使用する通常のPythonプログラムでは、コピーモジュールは必要ありません。広告:私は私の答えでこれらの点のいくつかに触れました。 :) – MSeifert

1

私は非常に詳細で他の回答で説明したように__copy__を上書きすることは、良いアイデアだと思います。代替ソリューションと呼ばれるもので、絶対に明確であることを明示的なコピー方法を書くことかもしれません:どんな未来読者がt.copy()を見たとき

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

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

彼はその後すぐに、カスタムコピー方法が実装されていることを知っています。欠点はもちろん、copy.copy(t)を使用するサードパーティのライブラリではうまくいかない可能性があります。

+0

"あなたはコピーかディープコピーを上書きすべきですか?"タイトルから....もどちらも 'copy.copy'を一度も忘れていませんか? –

+0

これは単なる代替案です。私はそれにはいくつかの利点があると思うが、私はまた、否定的な意味合いを完全に認識している。 –

+2

ok '__copy__'が定義されている場合は、' copy.copy(t) 'を実行したときに呼び出されます。それでも私はあなたが本当に質問された質問に対する解決策を提示していないようです。 (私はdownvoterはその理由のためだと思う) –

3

実際には、何を上書きするかを決定する要因(たとえば、__copy__または__deepcopy__)がクラスの望ましい動作に依存します。一般copy.deepcopy

は、ほとんどが正常に動作しますが、それだけでコピーすべて(recursivly)ので、あなただけがコピーされてはならないことを、いくつかの属性がある場合、それを上書きする必要があります(今までが!)。

一方、ユーザー(自分自身を含む)がコピーされたインスタンスに変更が反映されることを期待しない場合のみ、__copy__を定義する必要があります。たとえば、変更可能な型(例:list)をラップするか、変更可能な型を実装の詳細として使用するだけです。

コピーする属性の最小限のセットが明確に定義されていない場合もあります。その場合、私はまた__copy__をオーバーライドするかもしれませんが、おそらくそこにTypeErrorを上げ、可能な1つ(または複数)の専用公開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 

約期待値:

ユーザーは一般的に帽子を使用すると、コンストラクタにインスタンスを渡して最小限のコピー(__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__を上書きするだけです。

関連する問題