Pythonでオブジェクトのコピーを作成するにはどうすればよいですか?


199

オブジェクトのコピーを作成したいのですが。新しいオブジェクトに古いオブジェクトのすべてのプロパティ(フィールドの値)を持たせたい。しかし、私は独立したオブジェクトを持ちたいです。したがって、新しいオブジェクトのフィールドの値を変更しても、古いオブジェクトはその影響を受けません。

回答:


179

オブジェクトの完全に独立したコピーを取得するには、copy.deepcopy()関数を使用できます。

浅いコピーと深いコピーの詳細については、この質問に対する他の回答と、関連する質問に対するこの回答の説明を参照してください。


2
この回答は「回答なし」としてフラグが付けられ、削除され、元に戻さ
アーロンホール

@AaronHall教えてくれてありがとう!これは確かに私が書いた最大の答えではありませんが、強制的に削除してはならないという決定にはある程度同意します。少しブラッシュアップしますが、詳細(特にあなたの詳細)についてはすでに回答があるので、短くしておきます。
Sven Marnach

70

Pythonでオブジェクトのコピーを作成するにはどうすればよいですか?

したがって、新しいオブジェクトのフィールドの値を変更しても、古いオブジェクトはその影響を受けません。

つまり、変更可能なオブジェクトを意味します。

Python 3では、リストはcopyメソッドを取得します(2では、スライスを使用してコピーを作成します)。

>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True

浅いコピー

浅いコピーは、最も外側のコンテナのコピーです。

list.copy 浅いコピーです:

>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

内部オブジェクトのコピーは取得できません。それらは同じオブジェクトです-したがって、それらが変更されると、変更は両方のコンテナに表示されます。

深いコピー

ディープコピーは、各内部オブジェクトの再帰的なコピーです。

>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

変更はオリジナルには反映されず、コピーにのみ反映されます。

不変オブジェクト

不変オブジェクトは通常、コピーする必要はありません。実際、Pythonは元のオブジェクトを提供するだけです。

>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'

タプルにはcopyメソッドさえないので、スライスで試してみましょう:

>>> tuple_copy_attempt = a_tuple[:]

しかし、同じオブジェクトであることがわかります。

>>> tuple_copy_attempt is a_tuple
True

同様に文字列の場合:

>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True

そして、frozensetsの場合、copyメソッドはありますが:

>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True

不変オブジェクトをいつコピーするか

変更可能な内部オブジェクトをコピーする必要がある場合、不変オブジェクトコピーする必要があります。

>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)

ご覧のとおり、コピーの内部オブジェクトが変更されて、元のオブジェクトは変更されませ

カスタムオブジェクト

カスタムオブジェクトは通常、__dict__属性または__slots__(タプルのようなメモリ構造)にデータを格納します。

コピー可能なオブジェクトを作成するには、__copy__(浅いコピーの場合)および/または__deepcopy__(深いコピーの場合)を定義します。

from copy import copy, deepcopy

class Copyable:
    __slots__ = 'a', '__dict__'
    def __init__(self, a, b):
        self.a, self.b = a, b
    def __copy__(self):
        return type(self)(self.a, self.b)
    def __deepcopy__(self, memo): # memo is a dict of id's to copies
        id_self = id(self)        # memoization avoids unnecesary recursion
        _copy = memo.get(id_self)
        if _copy is None:
            _copy = type(self)(
                deepcopy(self.a, memo), 
                deepcopy(self.b, memo))
            memo[id_self] = _copy 
        return _copy

deepcopyは、id(original)コピーする(またはID番号)のメモ化辞書を保持していることに注意してください。再帰的なデータ構造で良好な動作を楽しむには、コピーがまだ作成されていないことを確認し、コピーがある場合はそれを返します。

それでは、オブジェクトを作成しましょう:

>>> c1 = Copyable(1, [2])

そしてcopy、浅いコピーを作成します:

>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]

そしてdeepcopy今度は深いコピーを作ります:

>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]

10

浅いコピー copy.copy()

#!/usr/bin/env python3

import copy

class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]

# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]

# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]

との深いコピー copy.deepcopy()

#!/usr/bin/env python3
import copy
class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]

ドキュメント:https : //docs.python.org/3/library/copy.html

Python 3.6.5でテスト済み。


-2

私は、Pythonでクラス化された多くの適切に動作するクラスで、以下が機能するはずだと思います。

def copy(obj):
    return type(obj)(obj)

(もちろん、ここでは「ディープコピー」については話をしていません。これは別の話であり、あまり明確な概念ではない可能性があります-深さは十分に深いですか?)

Python 3での私のテストによると、タプルや文字列などの不変オブジェクトの場合、同じオブジェクトを返します(不変オブジェクトの浅いコピーを作成する必要がないため)が、リストまたは辞書の場合は、独立した浅いコピーを作成します。

もちろん、このメソッドは、コンストラクターがそれに応じて動作するクラスに対してのみ機能します。可能な使用例:標準のPythonコンテナークラスの浅いコピーを作成します。


それはすばらしいことですが、カスタムクラスのコピー機能が失敗し、質問はオブジェクトに関するものだったため、質問には答えません。
Jared Smith

@JaredSmith、質問がすべてのオブジェクトに関するものであるとは述べられていませんでした。それが深いコピーなのか浅いコピーなのかさえ明確ではありませんでした(私は通常の浅いコピーを想定しますが、受け入れられた答えは深いものについてです)。カスタムクラスについては、それらがあなたのものである場合、__init__メソッドでこの種の規則を尊重するだけでかまいません。だから、私はこの方法は特定の目的には十分かもしれないと思いました。いずれにせよ、私はこの提案に関する有益なコメントに興味があります。
Alexey

class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = arg基本を考えてください 。私が行う場合、最も基本的なクラスでも関数が壊れているfoo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>という意味ですcopy。繰り返しますが、それはきちんとしたトリック(したがってDVがない)ですが、答えではありません。
Jared Smith

@JaredSmith、copy.copy浅いコピーを作成するためのメソッドがあることを確認しましたが、単純に言えば、「浅いコピーコンストラクタ」を提供するのはクラスの責任であるように思えます。そのような場合には、なぜそのようにインタフェースの同じkinfを提供していないdictlistのですか?それで、もしあなたのクラスがそのオブジェクトをコピーする責任を負うことを望むなら、なぜif isinstance(arg, type(self))句を追加しないの__init__ですか?
Alexey

1
常にクラスを制御できるとは限らないため、定義したクラスを使用する方法を使用します。それらは、1つの例として、Pythonバインディング(たとえば、GTK、openalpr、コアの一部)を持つCプログラムである場合があります。言うまでもなく、サードパーティのライブラリを使用してすべてのクラスにコピーメソッドを追加した場合でも、それを依存関係管理にどのように織り込みますか?
Jared Smith
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.