[python] 얕은 복사, 딥 카피 및 일반 할당 작업의 차이점은 무엇입니까?

import copy

a = "deepak"
b = 1, 2, 3, 4
c = [1, 2, 3, 4]
d = {1: 10, 2: 20, 3: 30}

a1 = copy.copy(a)
b1 = copy.copy(b)
c1 = copy.copy(c)
d1 = copy.copy(d)


print("immutable - id(a)==id(a1)", id(a) == id(a1))
print("immutable - id(b)==id(b1)", id(b) == id(b1))
print("mutable - id(c)==id(c1)", id(c) == id(c1))
print("mutable - id(d)==id(d1)", id(d) == id(d1))

다음과 같은 결과가 나타납니다.

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

심도 복사를 수행하는 경우 :

a1 = copy.deepcopy(a)
b1 = copy.deepcopy(b)
c1 = copy.deepcopy(c)
d1 = copy.deepcopy(d)

결과는 같습니다 :

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

할당 작업을 수행하는 경우 :

a1 = a
b1 = b
c1 = c
d1 = d

결과는 다음과 같습니다.

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) True
mutable - id(d)==id(d1) True

누군가가 사본 사이의 차이점을 정확히 설명 할 수 있습니까? 가변 및 불변 개체와 관련이 있습니까? 그렇다면 설명해 주시겠습니까?



답변

일반 할당 작업은 단순히 새 변수를 기존 객체를 향하게합니다. 문서는 얕은 및 깊은 사본의 차이를 설명 :

얕은 복사와 깊은 복사의 차이점은 복합 객체 (목록 또는 클래스 인스턴스와 같은 다른 객체를 포함하는 객체)에만 해당됩니다.

  • 얕은 복사본은 새 복합 개체를 구성한 다음 가능한 한 원본 개체에서 찾은 개체에 참조를 삽입합니다.

  • 딥 카피는 새 복합 객체를 구성한 다음 재귀 적으로 원본에서 찾은 객체의 카피를 복사합니다.

다음은 약간의 데모입니다.

import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]

일반 할당 작업을 사용하여 복사 :

d = c

print id(c) == id(d)          # True - d is the same object as c
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

얕은 사본 사용하기 :

d = copy.copy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

딥 카피 사용 :

d = copy.deepcopy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # False - d[0] is now a new object


답변

불변 개체의 경우 데이터가 변경되지 않기 때문에 복사가 필요하지 않으므로 Python은 동일한 데이터를 사용합니다. ID는 항상 같습니다. 변경 가능한 객체의 경우 잠재적으로 변경 될 수 있으므로 [얕은] 복사는 새 객체를 만듭니다.

딥 카피는 중첩 구조와 관련이 있습니다. 리스트리스트가있는 경우 copies중첩리스트도 딥 카피하므로 재귀 적 사본입니다. 복사 만하면 새로운 외부 목록이 있지만 내부 목록은 참조입니다.

과제는 복사되지 않습니다. 단순히 이전 데이터에 대한 참조를 설정합니다. 따라서 동일한 내용으로 새 목록을 만들려면 복사해야합니다.


답변

변경 불가능한 객체의 경우 복사본을 만들면 변경되지 않으므로 의미가 없습니다. 변경 가능한 객체의 경우 assignment, copydeepcopy다르게 동작합니다. 각각의 예에 대해 이야기 해 봅시다.

할당 작업은 단순히 소스의 참조를 대상에 할당합니다.

>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical

이제 ij기술적으로 동일한 목록을 의미합니다. 모두 ij같은 메모리 주소를 가지고있다. 둘 중 하나에 대한 업데이트는 다른 사람에게 반영됩니다. 예 :

>>> i.append(4)
>>> j
>>> [1,2,3,4] #Destination is updated

>>> j.append(5)
>>> i
>>> [1,2,3,4,5] #Source is updated

반면에 copydeepcopy변수의 새 복사본을 생성합니다. 따라서 원래 변수에 대한 변경 사항은 복사 변수에 반영되지 않으며 그 반대도 마찬가지입니다. 그러나 copy(shallow copy)중첩 된 객체의 복사본을 만들지 말고 중첩 된 객체의 참조 만 복사하면됩니다. Deepcopy는 중첩 된 모든 객체를 재귀 적으로 복사합니다.

몇 가지 예는 다음의 행동을 설명하는 copy과를 deepcopy:

다음을 사용하는 단순 목록 예 copy:

>>> import copy
>>> i = [1,2,3]
>>> j = copy.copy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Updation of original list didn't affected copied variable

다음을 사용하는 중첩 목록 예제 copy:

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.copy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x10296f908') #Nested lists have same address

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5,6]] #Updation of original nested list updated the copy as well

다음을 사용하는 단순 목록 예 deepcopy:

>>> import copy
>>> i = [1,2,3]
>>> j = copy.deepcopy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Updation of original list didn't affected copied variable

다음을 사용하는 중첩 목록 예제 deepcopy:

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.deepcopy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x102b9b7c8') #Nested lists have different addresses

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5]] #Updation of original nested list didn't affected the copied variable    


답변

그래픽 예제에서 다음 코드가 어떻게 실행되는지 봅시다 :

import copy

class Foo(object):
    def __init__(self):
        pass


a = [Foo(), Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)

여기에 이미지 설명을 입력하십시오


답변

a, b, c, d, a1, b1, c1 및 d1은 메모리의 객체에 대한 참조이며, ID로 고유하게 식별됩니다.

할당 작업은 메모리의 객체에 대한 참조를 가져와 해당 참조를 새 이름에 할당합니다. c=[1,2,3,4]는 4 개의 정수를 포함하는 새 목록 객체를 만들고 해당 객체에 대한 참조를에 할당하는 할당 c입니다. 동일한 객체에 대한 동일한 참조를c1=c 가져와 할당하는 할당입니다 c1. 목록은 변경 가능하기 때문에 목록을 통해 액세스하는지 c또는에 관계없이 해당 목록에서 발생하는 모든 내용이 표시됩니다 c1. 둘 다 동일한 객체를 참조하기 때문입니다.

c1=copy.copy(c)새 목록을 만들고 새 목록에 대한 참조를에 할당하는 “얕은 복사본”입니다 c1. c여전히 원본 목록을 가리 킵니다. 따라서에서 목록을 수정 c1해도 c참조 하는 목록은 변경되지 않습니다.

복사의 개념은 정수 및 문자열과 같은 변경 불가능한 객체와 관련이 없습니다. 이러한 객체를 수정할 수 없으므로 서로 다른 위치의 메모리에 동일한 값의 사본 두 개를 가질 필요가 없습니다. 따라서 정수와 문자열 및 복사 개념이 적용되지 않는 다른 객체는 단순히 재 지정됩니다. 이것은 왜 당신의 예제와 함께입니다 ab동일한 IDS의 결과.

c1=copy.deepcopy(c)“딥 카피”이지만이 예에서는 얕은 카피와 동일하게 작동합니다. 딥 카피는 얕은 카피가 객체 자체의 새로운 카피를 만들지 만 그 객체 내부의 모든 참조 는 스스로 복사되지 않는다는 점에서 얕은 카피와 다릅니다 . 귀하의 예에서, 당신의 목록에는 그 안에 정수가 있고 (불변), 앞에서 논의했듯이 그것들을 복사 할 필요가 없습니다. 따라서 딥 카피의 “딥”부분은 적용되지 않습니다. 그러나 이보다 복잡한 목록을 고려하십시오.

e = [[1, 2],[4, 5, 6],[7, 8, 9]]

이것은 다른 목록을 포함하는 목록입니다 (2 차원 배열로도 설명 할 수 있음).

에서 “shallow copy”를 실행하여에 e복사 e1하면 목록의 id가 변경되지만 목록의 각 사본에는 동일한 세 개의 목록 (정수가 포함 된 목록)에 대한 참조가 포함됩니다. 즉, 당신이해야 e[0].append(3)한다면 e그렇게 될 것입니다 [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. 그러나 e1또한 될 것 [[1, 2, 3],[4, 5, 6],[7, 8, 9]]입니다. 이후에했던 다른 한편, 만약 e.append([10, 11, 12]), e될 것이다 [[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]. 그러나 e1여전히 그렇습니다 [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. 외부 목록은 처음에 각각 3 개의 내부 목록에 대한 3 개의 참조를 포함하는 별도의 객체이기 때문입니다. 내부 목록을 수정하면 한 사본 또는 다른 사본을 통해 볼 때 변경 사항을 볼 수 있습니다. 그러나 위와 같이 외부 목록 중 하나를 수정하면e원본 세 목록에 대한 세 개의 참조와 새 목록에 대한 하나의 참조가 포함되어 있습니다. 그리고 e1여전히 원래 세 개의 참조 만 포함합니다.

‘심층 복사’는 외부 목록을 복제 할뿐만 아니라 목록 내부로 이동하여 내부 목록을 복제하여 결과 객체 두 개에 동일한 참조가 포함되지 않도록 (변형 가능한 객체에 관한 한) . 내부 목록에 추가 목록 (또는 사전과 같은 다른 개체)이 있으면 그 목록도 복제됩니다. 이것이 ‘딥 카피’의 ‘딥’부분입니다.


답변

파이썬에서 list, tuples, dict 등과 같은 객체를 일반적으로 ‘=’기호로 다른 객체에 할당하면 python은 참조로 copy를 만듭니다 . 즉, 다음과 같은 목록이 있다고 가정 해 봅시다.

list1 = [ [ 'a' , 'b' , 'c' ] , [ 'd' , 'e' , 'f' ]  ]

우리는이 목록에 다음과 같이 다른 목록을 할당합니다.

list2 = list1

파이썬 터미널에 list2를 인쇄하면 다음과 같이됩니다.

list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]

list1과 list2는 모두 동일한 메모리 위치를 가리키고 있습니다. 어느 하나를 변경하면 두 객체 모두에서 변경 사항이 표시됩니다. 즉, 두 객체 모두 동일한 메모리 위치를 가리 킵니다. list1을 다음과 같이 변경하면 :

list1[0][0] = 'x’
list1.append( [ 'g'] )

list1과 list2는 다음과 같습니다.

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g'] ]
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g’ ] ]

이제 얕은 복사를 통해 두 객체가 얕은 복사를 통해 복사 될 때 두 부모 객체의 자식 객체는 동일한 메모리 위치를 참조하지만 복사 된 객체의 새로운 변경 사항은 서로 독립적입니다. 작은 예제로 이것을 이해합시다. 이 작은 코드 스 니펫이 있다고 가정 해보십시오.

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]      # assigning a list
list2 = copy.copy(list1)       # shallow copy is done using copy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

list2는 영향을받지 않지만 다음과 같이 자식 객체를 변경하면 :

list1[0][0] = 'x’

list1과 list2는 모두 변경됩니다.

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] ]

이제 딥 카피 는 서로 완전히 고립 된 객체를 만드는 데 도움이됩니다. Deep Copy를 통해 두 객체를 복사하면 부모와 자식 모두 다른 메모리 위치를 가리 킵니다. 예 :

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]         # assigning a list
list2 = deepcopy.copy(list1)       # deep copy is done using deepcopy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

list2는 영향을받지 않지만 다음과 같이 자식 객체를 변경하면 :

list1[0][0] = 'x’

그러면 모든 자식 객체와 부모 객체가 다른 메모리 위치를 가리 키기 때문에 list2는 영향을받지 않습니다.

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f  ' ] ]

도움이 되길 바랍니다.


답변

아래 코드는 할당, 복사 방법을 사용한 얕은 복사, (슬라이스) [:]를 사용한 얕은 복사 및 심도의 차이를 보여줍니다. 아래 예제는 차이점을보다 분명하게 보여줌으로써 중첩 된 목록을 사용합니다.

from copy import deepcopy

########"List assignment (does not create a copy) ############
l1 = [1,2,3, [4,5,6], [7,8,9]]
l1_assigned = l1

print(l1)
print(l1_assigned)

print(id(l1), id(l1_assigned))
print(id(l1[3]), id(l1_assigned[3]))
print(id(l1[3][0]), id(l1_assigned[3][0]))

l1[3][0] = 100
l1.pop(4)
l1.remove(1)


print(l1)
print(l1_assigned)
print("###################################")

########"List copy using copy method (shallow copy)############

l2 = [1,2,3, [4,5,6], [7,8,9]]
l2_copy = l2.copy()

print(l2)
print(l2_copy)

print(id(l2), id(l2_copy))
print(id(l2[3]), id(l2_copy[3]))
print(id(l2[3][0]), id(l2_copy[3][0]))
l2[3][0] = 100
l2.pop(4)
l2.remove(1)


print(l2)
print(l2_copy)

print("###################################")

########"List copy using slice (shallow copy)############

l3 = [1,2,3, [4,5,6], [7,8,9]]
l3_slice = l3[:]

print(l3)
print(l3_slice)

print(id(l3), id(l3_slice))
print(id(l3[3]), id(l3_slice[3]))
print(id(l3[3][0]), id(l3_slice[3][0]))

l3[3][0] = 100
l3.pop(4)
l3.remove(1)


print(l3)
print(l3_slice)

print("###################################")

########"List copy using deepcopy ############

l4 = [1,2,3, [4,5,6], [7,8,9]]
l4_deep = deepcopy(l4)

print(l4)
print(l4_deep)

print(id(l4), id(l4_deep))
print(id(l4[3]), id(l4_deep[3]))
print(id(l4[3][0]), id(l4_deep[3][0]))

l4[3][0] = 100
l4.pop(4)
l4.remove(1)

print(l4)
print(l4_deep)
print("##########################")
print(l4[2], id(l4[2]))
print(l4_deep[3], id(l4_deep[3]))

print(l4[2][0], id(l4[2][0]))
print(l4_deep[3][0], id(l4_deep[3][0]))