아직 파이썬이 익숙하지 않은 나에게 반성문처럼 적어보는 글이다.
두번다시 헷갈리지 마라..
단일값 복사는 가볍게 해도 괜찮지만 nested 구조일 때는 데이터 복사할 때 주의해야 한다. 최상위 값만 복사되고 이후 값들은 누락될 가능성이 있기 때문이다.
그리고 파이썬에는 포인터가 따로 없기 때문에 각 메소드의 메모리 참조 방식을 알고 있어야 한다.
솔직히 이렇게 정리할 필요는 없고 옆에 gpt 선생님만 두면 된다. “메소드 사용 전에 gpt를 습관화하자..ㅎ”
1. .copy()는 얕은 복사다
img_copy = rgb_img.copy()
와 img_copy = rgb_img
의 차이는 메모리 참조 방식이다.
1.1. img_copy = rgb_img.copy()
rgb_img
의 복사본을 만들어 img_copy
에 저장
copy()
메소드: 새로운 메모리 공간에 동일한 데이터를 가진 독립적인 이미지가 생성
1.2. img_copy = rgb_img
img_copy
가 rgb_img
를 참조
1.3. 예제
import cv2
import numpy as np
# 원본 이미지
rgb_img = np.zeros((100, 100, 3), dtype=np.uint8) # 예제로 검정색 이미지 생성
rgb_img[25:75, 25:75] = (255, 0, 0) # 중앙 부분에 파란색 사각형 추가
# 참조만 하는 경우
img_ref = rgb_img
img_ref[0:50, 0:50] = (0, 255, 0) # 좌상단에 초록색 사각형 추가
# 복사본을 만드는 경우
img_copy = rgb_img.copy()
img_copy[50:100, 50:100] = (0, 0, 255) # 우하단에 빨간색 사각형 추가
# 결과 확인
print("Original Image:\n", rgb_img)
print("Referenced Image:\n", img_ref)
print("Copied Image:\n", img_copy)
2. .update()는 얕은 병합이며, 값이 중복되면 덮어쓴다.
2.1. img_dict.update(rgb_dict)
rgb_dict
의 키-값 쌍을 img_dict
에 병합
update()
메소드: img_dict
에 있는 기존 데이터는 그대로 유지하면서 rgb_dict
의 내용을 추가하거나, 동일한 키가 있으면 그 값을 덮어쓴다.
2.2. img_dict = rgb_dict
img_dict
가 rgb_dict
를 가리키도록 참조를 변경
2.3. 예제
# 기존 데이터가 있는 img_dict
img_dict = {'a': 1, 'b': 2}
rgb_dict = {'b': 3, 'c': 4}
# update를 사용하는 경우
img_dict.update(rgb_dict)
print("After update:", img_dict) # {'a': 1, 'b': 3, 'c': 4}
# 새롭게 참조하는 경우
img_dict = {'a': 1, 'b': 2}
img_dict = rgb_dict
print("After reference assignment:", img_dict) # {'b': 3, 'c': 4}
print("Original rgb_dict:", rgb_dict) # {'b': 3, 'c': 4}
3. Shallow vs Deep
=
, copy()
, deepcopy()
, 그리고 update()
는 파이썬에서 객체를 복사하거나 데이터 구조를 수정하는 방식에 따라 서로 다르게 동작한다. 각 방법은 객체의 독립성과 참조 방식에 따라 다르며, 특히 중첩된 구조에서 다양한 결과를 나타낸다.
3.1. = 연산자: 참조 복사 (Shallow Reference)
=
연산자는 변수가 기존 객체를 참조하도록 만드는 방식으로, 새로운 변수가 동일한 객체를 참조하기 때문에, 한 변수를 수정하면 다른 변수에도 반영된다.
- 특징: 두 변수가 동일한 객체를 참조하여 메모리를 공유함
- 동작: 한 변수를 변경하면 다른 변수에도 영향을 미침
a = [1, 2, 3]
b = a # a와 b는 같은 리스트 객체를 참조
b.append(4)
print(a) # 출력: [1, 2, 3, 4]
3.2. copy() 메소드: 얕은 복사 (Shallow Copy)
copy()
는 객체의 최상위 계층만 복사하는 얕은 복사를 수행한다. 최상위 객체는 독립적이지만, 내부 중첩 객체들은 여전히 원본을 참조한다.
- 특징: 최상위 객체는 독립적이지만, 중첩 객체들은 원본과 참조를 공유함
- 동작: 최상위 수준에서의 변경은 독립적이나, 중첩 객체의 변경은 원본과 복사본 모두에 반영됨
import copy
a = [[1, 2, 3], [4, 5, 6]]
b = a.copy() # 얕은 복사
b[0].append(7)
print(a) # 출력: [[1, 2, 3, 7], [4, 5, 6]]
print(b) # 출력: [[1, 2, 3, 7], [4, 5, 6]]
3.3. deepcopy() 메소드: 깊은 복사 (Deep Copy)
deepcopy()
는 객체의 모든 계층을 재귀적으로 복사하여, 원본 객체와 완전히 독립적인 복사본을 만든다. 중첩된 객체들까지 모두 새롭게 생성되므로, 복사본과 원본은 전혀 영향을 주지 않는다.
- 특징: 모든 중첩 객체가 독립적이며 원본과 완전히 분리됨
- 동작: 복사본에 대한 모든 변경이 원본에 영향을 미치지 않음
import copy
a = [[1, 2, 3], [4, 5, 6]]
b = copy.deepcopy(a)
b[0].append(7)
print(a) # 출력: [[1, 2, 3], [4, 5, 6]]
print(b) # 출력: [[1, 2, 3, 7], [4, 5, 6]]
3.4. update() 메소드: 얕은 병합 (Shallow Merge)
update()
는 딕셔너리에서 다른 딕셔너리의 키-값 쌍을 추가하거나 기존 값을 덮어쓰는 방법으로 사용된다. 최상위 객체는 병합되지만, 내부 중첩 객체는 원본과 참조를 공유하는 얕은 복사 방식이다.
- 특징:
update()
는 최상위 키-값 쌍을 병합하지만, 중첩된 객체에 대해서는 원본 참조를 유지 - 동작: 새로 추가되거나 덮어쓴 키의 값은 독립적이지만, 중첩 객체의 경우 원본과 참조가 공유됨
a = {'key1': [1, 2], 'key2': [3, 4]}
b = {'key2': [5, 6], 'key3': [7, 8]}
a.update(b) # a에 b의 내용을 병합
print(a) # 출력: {'key1': [1, 2], 'key2': [5, 6], 'key3': [7, 8]}
print(b) # 출력: {'key2': [5, 6], 'key3': [7, 8]} b['key2'].append(9)
print(a) # 출력: {'key1': [1, 2], 'key2': [5, 6, 9], 'key3': [7, 8]}
3.5. 깊은 병합 (Deep Merge)
깊은 병합은 딕셔너리의 모든 계층을 재귀적으로 병합하여 중첩된 구조까지 독립적인 객체로 결합하는 방식이다.
기본적으로 얕은 병합만 지원하므로, 깊은 병합을 하려면 재귀적 merge 함수를 직접 작성하거나, 외부 라이브러리를 사용해야 한다.
def deep_merge(dict1, dict2):
merged = dict1.copy()
for key, value in dict2.items():
if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
merged[key] = deep_merge(merged[key], value) # 재귀적 병합
else:
merged[key] = value # 새 키나 덮어쓰기가 아닌 경우 직접 추가
return merged
# 예제 딕셔너리
a = {'key1': {'subkey1': 1, 'subkey2': 2}, 'key2': [1, 2, 3]}
b = {'key1': {'subkey2': 'updated', 'subkey3': 3}, 'key3': 'new_value'}
# 깊은 병합 수행
merged_dict = deep_merge(a, b)
print(merged_dict)
# 출력
'''
{
'key1': {'subkey1': 1, 'subkey2': 'updated', 'subkey3': 3},
'key2': [1, 2, 3],
'key3': 'new_value'
}
'''
3.6. 요약
복사 방식 | 동작 설명 | 예시 |
= (참조 복사) | 새로운 변수가 기존 객체를 참조하도록 설정. 같은 객체를 가리킴 | b = a |
copy() (얕은 복사) | 최상위 객체는 복사하지만, 중첩된 객체는 원본과 참조를 공유. | b = a.copy() |
deepcopy() (깊은 복사)
|
모든 계층을 독립적인 객체로 복사하여, 원본과 완전히 독립된 객체를 생성함 |
b = copy.deepcopy(a)
|
update() (얕은 병합)
|
최상위 키-값 쌍을 병합하며 중첩된 객체는 원본과 참조를 공유 | a.update(b) |
=
- 메모리를 공유하므로, 한쪽에서의 수정이 다른 쪽에도 영향 줌
copy()
- 최상위 객체에서만 독립적이며, 중첩된 객체를 수정하면 원본과 복사본 모두에 영향 줌
deepcopy()
- 모든 계층을 복사해 독립적인 객체를 만드므로, 서로 완전히 독립적
update()
- 최상위 수준에서 병합되지만, 중첩 객체는 참조가 공유되어 한쪽에서 수정하면 다른 쪽에도 영향 줌
