[python] Python 객체에 대한 복사 / 딥 카피 작업을 재정의하는 방법은 무엇입니까?

나는 사이의 차이를 이해 copydeepcopy복사 모듈을. 내가 사용했습니다 copy.copycopy.deepcopy이전을 성공적으로, 그러나 이것은 내가 실제로 과부하에 대해 갔어요 처음 __copy____deepcopy__방법을. 이미 통해 주위 구글에서 보니 한의 인스턴스를 찾기 위해 파이썬 모듈 내장 __copy____deepcopy__기능 (예를 들어 sets.py, decimal.pyfractions.py),하지만 난 아직 100 % 확인 내가 잘있어 해요.

내 시나리오는 다음과 같습니다.

구성 개체가 있습니다. 처음에는 기본 값 집합으로 하나의 구성 개체를 인스턴스화 할 것입니다. 이 구성은 여러 다른 개체에 전달됩니다 (모든 개체가 동일한 구성으로 시작되도록 함). 그러나 사용자 상호 작용이 시작되면 각 개체는 서로의 구성에 영향을주지 않고 독립적으로 구성을 조정해야합니다 (이는 나에게 초기 구성의 딥 카피를 작성해야한다고 말합니다).

다음은 샘플 개체입니다.

class ChartConfig(object):

    def __init__(self):

        #Drawing properties (Booleans/strings)
        self.antialiased = None
        self.plot_style = None
        self.plot_title = None
        self.autoscale = None

        #X axis properties (strings/ints)
        self.xaxis_title = None
        self.xaxis_tick_rotation = None
        self.xaxis_tick_align = None

        #Y axis properties (strings/ints)
        self.yaxis_title = None
        self.yaxis_tick_rotation = None
        self.yaxis_tick_align = None

        #A list of non-primitive objects
        self.trace_configs = []

    def __copy__(self):
        pass

    def __deepcopy__(self, memo):
        pass 

적절한 동작 을 보장 하고 제공 하기 위해이 개체에 copydeepcopy메서드 를 구현하는 올바른 방법은 무엇입니까 ?copy.copycopy.deepcopy



답변

맞춤 설정에 대한 권장 사항은 문서 페이지 의 맨 끝에 있습니다 .

클래스는 동일한 인터페이스를 사용하여 산 세척을 제어하는 ​​데 사용하는 복사를 제어 할 수 있습니다. 이러한 메소드에 대한 정보는 모듈 pickle의 설명을 참조하십시오. 복사 모듈은 copy_reg 등록 모듈을 사용하지 않습니다.

클래스가 자체 복사 구현을 정의하기 위해 특수 메서드 __copy__()
__deepcopy__(). 전자는 얕은 복사 작업을 구현하기 위해 호출됩니다. 추가 인수가 전달되지 않습니다. 후자는 전체 복사 작업을 구현하기 위해 호출됩니다. 하나의 인수 인 메모 사전이 전달됩니다. 는 IF __deepcopy__()
구현이 구성 요소의 깊은 복사본을 만들 필요가있다, 그것은 호출해야합니다 deepcopy()첫 번째 인자와 두 번째 인수로 메모 사전과 같은 구성 요소와 기능을.

피클 링 커스터마이징에 신경 쓰지 않는 것 같기 때문에 정의 __copy__하고 __deepcopy__확실히 올바른 방법으로 보입니다.

특히, __copy__(얕은 사본) 귀하의 경우에는 매우 쉽습니다 … :

def __copy__(self):
  newone = type(self)()
  newone.__dict__.update(self.__dict__)
  return newone

__deepcopy__(A 받아들이는 유사하다 memo너무 인수를)하지만 반환하기 전에 호출해야 self.foo = deepcopy(self.foo, memo)하는 모든 속성에 대한 self.foo요구가 깊은 (컨테이너 본질적 속성을 복사하는 것을 – 목록, dicts을 통해 다른 물건을 개최 비 원시적 객체를 자신의__dict__ 들).


답변

Alex Martelli의 답변과 Rob Young의 의견을 합치면 다음 코드가 표시됩니다.

from copy import copy, deepcopy

class A(object):
    def __init__(self):
        print 'init'
        self.v = 10
        self.z = [2,3,4]

    def __copy__(self):
        cls = self.__class__
        result = cls.__new__(cls)
        result.__dict__.update(self.__dict__)
        return result

    def __deepcopy__(self, memo):
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v, memo))
        return result

a = A()
a.v = 11
b1, b2 = copy(a), deepcopy(a)
a.v = 12
a.z.append(5)
print b1.v, b1.z
print b2.v, b2.z

인쇄물

init
11 [2, 3, 4, 5]
11 [2, 3, 4]

여기 에서 객체 자체가 멤버에서 참조되는 경우 과도한 복사를 피하기 위해 dict를 __deepcopy__채 웁니다 memo.


답변

다음 베드로의 훌륭한 대답을 기본 구현에 최소한의 변경 (I 필요 같은 예를 들어 그냥 필드를 수정)과, 사용자 정의 deepcopy을 구현하기 위해 :

class Foo(object):
    def __deepcopy__(self, memo):
        deepcopy_method = self.__deepcopy__
        self.__deepcopy__ = None
        cp = deepcopy(self, memo)
        self.__deepcopy__ = deepcopy_method
        cp.__deepcopy__ = deepcopy_method

        # custom treatments
        # for instance: cp.id = None

        return cp


답변

복사 방법을 사용자 정의하고 싶지 않기 때문에 이러한 방법을 재정의해야하는 이유가 문제에서 명확하지 않습니다.

어쨌든 딥 카피를 사용자 정의하려면 (예 : 일부 속성을 공유하고 다른 항목을 복사하여) 다음과 같은 해결책이 있습니다.

from copy import deepcopy


def deepcopy_with_sharing(obj, shared_attribute_names, memo=None):
    '''
    Deepcopy an object, except for a given list of attributes, which should
    be shared between the original object and its copy.

    obj is some object
    shared_attribute_names: A list of strings identifying the attributes that
        should be shared between the original and its copy.
    memo is the dictionary passed into __deepcopy__.  Ignore this argument if
        not calling from within __deepcopy__.
    '''
    assert isinstance(shared_attribute_names, (list, tuple))
    shared_attributes = {k: getattr(obj, k) for k in shared_attribute_names}

    if hasattr(obj, '__deepcopy__'):
        # Do hack to prevent infinite recursion in call to deepcopy
        deepcopy_method = obj.__deepcopy__
        obj.__deepcopy__ = None

    for attr in shared_attribute_names:
        del obj.__dict__[attr]

    clone = deepcopy(obj)

    for attr, val in shared_attributes.iteritems():
        setattr(obj, attr, val)
        setattr(clone, attr, val)

    if hasattr(obj, '__deepcopy__'):
        # Undo hack
        obj.__deepcopy__ = deepcopy_method
        del clone.__deepcopy__

    return clone



class A(object):

    def __init__(self):
        self.copy_me = []
        self.share_me = []

    def __deepcopy__(self, memo):
        return deepcopy_with_sharing(self, shared_attribute_names = ['share_me'], memo=memo)

a = A()
b = deepcopy(a)
assert a.copy_me is not b.copy_me
assert a.share_me is b.share_me

c = deepcopy(b)
assert c.copy_me is not b.copy_me
assert c.share_me is b.share_me


답변

나는 세부 사항에 대해 약간 벗어나있을 수 있지만 여기에 있습니다.

로부터 copy문서 ;

  • 단순 복사는 새로운 복합 객체를 생성 한 다음 (가능한 한) 원본에서 찾은 객체에 대한 참조를 여기에 삽입합니다.
  • 전체 복사는 새로운 복합 개체를 생성 한 다음 재귀 적으로 원본에서 찾은 개체의 복사본을 삽입합니다.

copy(), 맨 위 요소 만 복사하고 나머지는 원래 구조에 대한 포인터로 남겨 둡니다.deepcopy()모든 것을 재귀 적으로 복사합니다.

그건, deepcopy() 필요한 것입니다.

정말 구체적인 작업을 수행해야하는 경우 설명서에 설명 된대로 __copy__()또는 을 재정의 할 수 있습니다 __deepcopy__(). 개인적으로 저는 아마도 config.copy_config()파이썬 표준 동작이 아니라는 것을 분명히하기 위해 (예를 들어) 평범한 함수를 구현할 것입니다 .


답변

copy모듈은 결국 사용 __getstate__()/ 산세 프로토콜 이 또한 재정에 유효한 목표 그래서.__setstate__()

기본 구현 __dict__은 클래스 의 를 반환하고 설정하기 때문에 super() 의 Eino Gourdin의 영리한 트릭 을 호출 하고 걱정할 필요가 없습니다 .


답변

Antony Hatchkins의 깨끗한 대답을 기반으로 한 내 버전은 문제의 클래스가 다른 사용자 정의 클래스에서 파생 된 것입니다 (을 호출해야 함 super).

class Foo(FooBase):
    def __init__(self, param1, param2):
        self._base_params = [param1, param2]
        super(Foo, result).__init__(*self._base_params)

    def __copy__(self):
        cls = self.__class__
        result = cls.__new__(cls)
        result.__dict__.update(self.__dict__)
        super(Foo, result).__init__(*self._base_params)
        return result

    def __deepcopy__(self, memo):
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            setattr(result, k, copy.deepcopy(v, memo))
        super(Foo, result).__init__(*self._base_params)
        return result