dict.copy () 이해하기-얕거나 깊습니까?


429

에 대한 설명서를 읽는 동안 dict.copy()사전의 얕은 사본을 만듭니다. 내가 따르는 책 (Beazley 's Python Reference)도 마찬가지입니다.

m.copy () 메소드는 맵핑 오브젝트에 포함 된 항목의 얕은 사본을 작성하여 새 맵핑 오브젝트에 배치합니다.

이걸 고려하세요:

>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}

그래서 나는 original얕은 사본을하고 있었기 때문에 이것이 값을 업데이트하고 'c': 3을 추가 한다고 가정했습니다 . 목록을 작성하는 것처럼 :

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])

이것은 예상대로 작동합니다.

둘 다 얕은 사본이므로 왜 dict.copy()예상대로 작동하지 않습니까? 아니면 얕은 대 깊은 복사에 대한 나의 이해가 잘못 되었습니까?


2
그들은 "얕은"을 설명하지 않는 기이 한. 내부자 지식, 윙크. dict 및 키만 사본이지만 첫 번째 레벨 내에 중첩 된 dicts는 참조이므로 루프에서 삭제할 수 없습니다. 따라서이 경우 Python의 dict.copy ()는 유용하거나 직관적이지 않습니다. 질문 해 주셔서 감사합니다.
gseattle

답변:


990

"얕은 복사" 는 사전 의 내용 이 값별로 복사되는 것이 아니라 새로운 참조를 만드는 것을 의미합니다 .

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

반대로 딥 카피는 모든 내용을 가치별로 복사합니다.

>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

그래서:

  1. b = a: 참조 지정, 작성 ab동일한 객체를 가리 킵니다.

    'a = b'의 그림 : 'a'와 'b'는 모두 '{1 : L}'을, 'L'은 '[1, 2, 3]'을 가리 킵니다.

  2. b = a.copy(): 얕은 복사 ab두 개의 분리 된 객체가되지만 내용은 여전히 ​​동일한 참조를 공유합니다.

    'b = a.copy ()'그림 : 'a'는 '{1 : L}', 'b'는 '{1 : M}', 'L'및 'M'은 모두 '[ 1, 2, 3] '.

  3. b = copy.deepcopy(a): 딥 카피 ab의 구조와 내용이 완전히 분리됩니다.

    'b = copy.deepcopy (a)'의 그림 : 'a'는 '{1 : L}'을 가리키고 'L'은 '[1, 2, 3]'을 가리 킵니다.;  'b'는 '{1 : M}'을 가리키고 'M'은 '[1, 2, 3]'의 다른 인스턴스를 가리 킵니다.


좋은 대답이지만 첫 문장에서 문법 오류를 수정하는 것을 고려할 수 있습니다. 그리고 L에서 다시 사용하지 않을 이유가 없습니다 b. 그렇게하면 예제가 간단 해집니다.
Tom Russell

@kennytm : 실제로 처음 두 예제의 차이점은 무엇입니까? 동일한 결과를 얻지 만 약간 다른 내부 구현이 이루어 지지만 중요한 것은 무엇입니까?
JavaSa

@TomRussell : 또는 누군가,이 질문이 아주 오래 되었기 때문에 나의 설명 질문은 모두를위한 것입니다
JavaSa

@JavaSa 예를 들어, 할 경우 중요합니다 b[1][0] = 5. b얕은 사본 인 경우 방금 변경했습니다 a[1][0].
톰 러셀

2
대단한 설명 .. 정말로 하루를 구했다! 감사합니다 ... 이것은 목록, str 및 기타 파이썬 데이터 유형에 적용될 수 있습니까?
부로

38

딥 카피 나 얕은 카피의 문제는 아니며 딥 카피도하지 않습니다.

여기:

>>> new = original 

원본에서 참조한 목록 / dict에 대한 새 참조를 작성하고 있습니다.

여기있는 동안 :

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)

원래 컨테이너에 포함 된 객체의 참조 사본으로 채워진 새 목록 / dict를 작성하고 있습니다.


31

이 예제를 보자 :

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()

이제 '얕은'(첫 번째) 레벨의 값을 변경해 보겠습니다.

new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer

이제 한 수준 더 깊이있는 값을 변경해 보겠습니다.

new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed

8
no change in original, since ['a'] is an immutable integer이. 실제로 질문에 대답합니다.
CivFan

8

kennytm의 답변에 추가. 얕은 복사 parent.copy () 를 수행하면 동일한 키로 새 사전이 작성되지만 값은 참조 되지 않습니다. parent_copy에 새 값을 추가하면 parent_copy 가 새 사전 이므로 상위에 영향을 미치지 않습니다. 참조하지 않습니다.

parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent

print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400

print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128

parent_copy[1].append(4)
parent_copy[2] = ['new']

print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}

parent [1] , parent_copy [1] 의 해시 (id) 값 은 동일하므로 id 140690938288400에 저장된 parent [1]parent_copy [1] 의 [1,2,3]을 의미 합니다.

그러나 parentparent_copy의 해시 는 다르며, 이는 다른 사전이며 parent_copyparent 값에 대한 값을 갖는 새로운 사전입니다.


5

"new"와 "original"은 다른 dicts이므로, 그중 하나만 업데이트 할 수 있습니다. 항목 은 dict 자체가 아니라 얕은 복사입니다.


2

내용 이 얕습니다.

따라서 원본 dict에 하나 list이상이 포함 dictionary된 경우 원본 또는 얕은 사본에서 수정 하면 다른 사본 ( list또는 dict) 이 수정됩니다 .


1

두 번째 부분에서는 new = original.copy()

.copy그리고 =다른 것들입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.