web-dev-qa-db-ja.com

Pythonでクラスインスタンスをコピーする方法は?

Pythonでクラスインスタンスのコピーを作成したいと思います。私は試した copy.deepcopyしかし、エラーメッセージが表示されます。

RuntimeError:現時点では、ユーザーが明示的に作成した変数(グラフから離れる)のみがディープコピープロトコルをサポートしています

だから私は次のようなものがあると仮定します:

class C(object):
    def __init__(self,a,b, **kwargs):
        self.a=a
        self.b=b
        for x, v in kwargs.items():
            setattr(self, x, v)

c = C(4,5,'r'=2)
c.a = 11
del c.b

そして今、cの同一のディープコピーを作成したいのですが、簡単な方法はありますか?

11
patapouf_ai

私はほとんどそれを理解しました。私が克服できない唯一の問題は、すべてのクラスの初期化引数の許容セット(__init__の引数)を知ることです。したがって、次の2つの仮定をする必要があります。

1)Cと呼ぶクラスargsCの一連のデフォルト引数があります。 2)Cのすべてのオブジェクトは、空の引数で初期化できます。

その場合、私は最初Cをコピーしたいインスタンスからcクラスの新しいインスタンスを初期化します:

c_copy = c.__class__(**argsC)

2番目cのすべての属性を調べ、c_copy属性をcの属性のコピーに設定します

for att in c.__dict__:
    setattr(c_copy, att, object_copy(getattr(c,att)))

ここで、object_copyは、作成中の関数の再帰的なアプリケーションです。

最後c_copyのすべての属性を削除しますが、cの属性は削除しません:

for att in c_copy.__dict__:
    if not hasattr(c, att):
        delattr(c_copy, att)

これをすべてまとめると、次のようになります。

import copy

def object_copy(instance, init_args=None):
    if init_args:
        new_obj = instance.__class__(**init_args)
    else:
        new_obj = instance.__class__()
    if hasattr(instance, '__dict__'):
        for k in instance.__dict__ :
            try:
                attr_copy = copy.deepcopy(getattr(instance, k))
            except Exception as e:
                attr_copy = object_copy(getattr(instance, k))
            setattr(new_obj, k, attr_copy)

        new_attrs = list(new_obj.__dict__.keys())
        for k in new_attrs:
            if not hasattr(instance, k):
                delattr(new_obj, k)
        return new_obj
    else:
        return instance

したがって、すべてをまとめると次のようになります。

argsC = {'a':1, 'b':1}
c = C(4,5,r=[[1],2,3])
c.a = 11
del c.b
c_copy = object_copy(c, argsC)
c.__dict__

{'a':11、 'r':[[1]、2、3]}

c_copy.__dict__

{'a':11、 'r':[[1]、2、3]}

c.__dict__

{'a':11、 'r':[[1、33]、2、3]}

c_copy.__dict__

{'a':11、 'r':[[1]、2、3]}

これが望ましい結果です。可能であればdeepcopyを使用しますが、例外が発生する場合は、それを使用せずに実行できます。

2
patapouf_ai

それを行う1つの方法は、__copy__Cクラスで次のようになります。

class A:
    def __init__(self):
        self.var1 = 1
        self.var2 = 2
        self.var3 = 3

class C(A):
    def __init__(self, a=None, b=None, **kwargs):
        super().__init__()
        self.a = a
        self.b = b
        for x, v in kwargs.items():
            setattr(self, x, v)

    def __copy__(self):
        self.normalizeArgs()
        return C(self.a, self.b, kwargs=self.kwargs)

    # THIS IS AN ADDITIONAL GATE-KEEPING METHOD TO ENSURE 
    # THAT EVEN WHEN PROPERTIES ARE DELETED, CLONED OBJECTS
    # STILL GETS DEFAULT VALUES (NONE, IN THIS CASE)
    def normalizeArgs(self):
        if not hasattr(self, "a"):
            self.a      = None
        if not hasattr(self, "b"):
            self.b      = None
        if not hasattr(self, "kwargs"):
            self.kwargs = {}

cMain   = C(a=4, b=5, kwargs={'r':2})

del cMain.b
cClone  = cMain.__copy__()

cMain.a = 11

del  cClone.b
cClone2 = cClone.__copy__()

print(vars(cMain))
print(vars(cClone))
print(vars(cClone2))

enter image description here

7
Poiz

はい、deepcopyを使用してクラスインスタンスのコピーを作成できます。

from copy import deepcopy

c = C(4,5,'r'=2)
d = deepcopy(c)

これにより、クラスインスタンス 'c'のコピーが 'd'に作成されます。

6
Usman