파이썬에서 객체의 복사본을 어떻게 만들 수 있습니까?


199

객체의 사본을 만들고 싶습니다. 새 객체가 이전 객체의 모든 속성 (필드 값)을 갖기를 원합니다. 그러나 나는 독립적 인 물건을 갖고 싶습니다. 따라서 새 객체의 필드 값을 변경하면 이전 객체에 영향을 미치지 않아야합니다.

답변:


179

완전히 독립적 인 객체 사본을 얻으려면 copy.deepcopy()함수를 사용할 수 있습니다 .

얕고 깊은 복사에 대한 자세한 내용은이 질문에 대한 다른 답변 과 관련 질문에 대한이 답변 의 좋은 설명을 참조하십시오 .


2
이 답변은 "답변이 아님"으로 표시, 삭제 및 삭제 취소되었습니다. 여기서 meta 토론 : meta.stackoverflow.com/questions/377844/…
Aaron Hall

@AaronHall 알려 주셔서 감사합니다! 이것은 확실히 내가 쓴 가장 큰 대답은 아니지만 강제로 삭제해서는 안된다는 결정에 동의합니다. 나는 그것을 조금 솔질 할 것이지만, 모든 세부 사항 (특히 당신의 것)에 대한 답변이 이미 있기 때문에 나는 그것을 짧게 유지할 것입니다.
Sven Marnach

70

파이썬에서 객체의 복사본을 어떻게 만들 수 있습니까?

따라서 새 객체의 필드 값을 변경하면 이전 객체에 영향을 미치지 않아야합니다.

그러면 변경 가능한 객체를 의미합니다.

파이썬 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'}}]

변경 사항은 원본에만 반영되지 않고 사본에만 반영됩니다.

불변의 객체

불변 개체는 일반적으로 복사 할 필요가 없습니다. 실제로, 당신이 시도하면, 파이썬은 당신에게 원래 객체를 줄 것입니다 :

>>> 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'

튜플에는 복사 방법이 없으므로 슬라이스로 시도해 보겠습니다.

>>> tuple_copy_attempt = a_tuple[:]

그러나 우리는 그것이 같은 대상이라는 것을 알았습니다.

>>> tuple_copy_attempt is a_tuple
True

문자열과 유사하게 :

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

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)사본 (또는 식별 번호). 재귀 데이터 구조로 좋은 동작을 즐기려면 이미 사본을 작성하지 않았는지 확인하십시오.

객체를 만들어 봅시다 :

>>> 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

다음은 파이썬에서 잘 동작하는 많은 클래스에서 작동해야한다고 생각합니다.

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

(물론, 여기서는 다른 이야기이며, 매우 명확한 개념이 아닐 수있는 "딥 카피 (deep copy)"에 대해 여기서 이야기하고 있지 않습니다. 깊이는 얼마나 깊습니까?)

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를 제공하지 않도록 dict하고 list있습니까? 따라서 클래스가 객체 복사에 대한 책임을지기를 원한다면 왜 if isinstance(arg, type(self))절 을 추가하지 __init__않습니까?
Alexey

1
클래스를 항상 제어 할 수있는 것은 아니므로 정의한 클래스를 사용하는 방식을 사용합니다. 예를 들어, 파이썬 바인딩 (예 : GTK, openalpr, 핵심 부분)이있는 C 프로그램 일 수 있습니다. 제 3 자 라이브러리를 가져 와서 모든 클래스에 복사 방법을 추가하더라도 종속성 관리에 어떻게 직조 할 것입니까?
Jared Smith
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.