나는 사이의 차이를 이해 copy
대 deepcopy
복사 모듈을. 내가 사용했습니다 copy.copy
및 copy.deepcopy
이전을 성공적으로, 그러나 이것은 내가 실제로 과부하에 대해 갔어요 처음 __copy__
과 __deepcopy__
방법을. 이미 통해 주위 구글에서 보니 한의 인스턴스를 찾기 위해 파이썬 모듈 내장 __copy__
및 __deepcopy__
기능 (예를 들어 sets.py
, decimal.py
및 fractions.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
적절한 동작 을 보장 하고 제공 하기 위해이 개체에 copy
및 deepcopy
메서드 를 구현하는 올바른 방법은 무엇입니까 ?copy.copy
copy.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