python 매직 - 파이썬에서 객체의 복사본을 만들려면 어떻게해야합니까?





메소드 (4)


copy.copy() 얕은 사본 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()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에서 테스트되었습니다.

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




다음은 Python으로 분류 된 잘 작동하는 많은 사람들과 작동해야한다고 생각합니다.

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

(물론, 나는 다른 이야기 인 "딥 카피 (deep copy)"에 관해서 이야기하는 것이 아닙니다. 그리고 그것은 아주 명확한 컨셉이 아닐 수도 있습니다 - 깊이가 얼마나 깊습니까?)

튜플이나 문자열 같은 불변 객체의 경우 Python 3을 사용하여 테스트 한 바에 따르면 동일한 객체를 반환하지만 (불변 객체의 얕은 복사본을 만들 필요가 없으므로) 목록이나 사전의 경우 독립된 얕은 복사본을 만듭니다. .

물론이 메서드는 생성자가 그에 따라 동작하는 클래스에서만 작동합니다. 가능한 사용 사례 : 표준 파이썬 컨테이너 클래스의 얕은 복사본 만들기.




파이썬에서 객체의 복사본을 만들려면 어떻게해야합니까?

따라서 새 객체의 필드 값을 변경하면 이전 객체가 영향을받지 않습니다.

그러면 가변 개체를 의미합니다.

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

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

deepcopyid(original) (또는 ID 번호)의 메모 사전을 사본에 보관합니다. 재귀 적 데이터 구조로 좋은 동작을 즐기려면 아직 복사본을 만들지 않았는지 확인하십시오.

그럼 객체를 만들어 봅시다.

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

그리고 copy 은 얕은 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]



Lodash에는 좋은 lodash.com/docs#cloneDeep 메서드가 있습니다.

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false




python oop object copy