- 할당 vs 얕은 복사 vs 깊은 복사
- 얕은 복사
- 객체의 최상위 레벨만 복사하고, 그 안에 포함된 하위 객체들은 원본 객체와 참조를 공유함
- 깊은 복사
- 객체와 그 안에 포함된 모든 하위 객체까지 재귀적으로 복사하여, 원본 객체와 완전히 독립된 새로운 객체를 생성함
- pandas에서는 copy 자체가 깊은복사임
- a = [1, [2, 3]] 가정
| 구분 | 1. 단순 할당 (b=a) | 2. 얕은 복사 (c=a.copy()) | 3. 깊은 복사 (d =copy.deepcopy(a)) |
| 개념 | 리모콘 공유 | 껍데기만 새것 | 전부 다 새것 |
| 새 주소 | a와 주소가 같음 | a와 주소가 다름 | a와 주소가 다름 |
| 내부 주소 | 내부 리스트 주소 같음 | 내부 리스트 주소 같음 | 내부 리스트 주소 다름 |
| 영향력 - 숫자 | b를 바꾸면 a가 바뀜 | c를 바꾸어도 a는 안바뀜 (immutable) | d를 어떻게 바꿔도 a는 안전함 |
| 영향력 - 리스트 | b를 바꾸면 a가 바뀜 | c를 바꾸면 a가 바뀜 (mutable) | d를 어떻게 바꿔도 a는 안전함 |
- a 안의 값을 수정한다고 할 때
- a 값 안의 숫자값은 immutable이기 때문에
- 얕은 복사나 깊은 복사를 한 경우엔 서로 다른 주소값을 가진 채로 a와 달리 변동 없음
- 예를들어, a의 숫자값이 1에서 7로 변동된다고 한다면
- 1을 가리키던 id(a[0])이 7을 가리키는 주소값으로 완전히 '변경'(갈아끼워짐)되기 때문!
- but c[0]은 여전히 1을 가리키는 주소값 그대로
- a 값 안의 리스트는 mutable이기 때문에
- 얕은 복사를 한 경우엔 a와 리스트의 주소값은 같은 것을 가리키고 있기 때문에
- a 안의 리스트가 수정된다면 c 안의 리스트 또한 변동됨
- 예를들어, a의 리스트가 [2,3]이었는데 a[1][0]=200으로 수정한다고 한다면
- c안의 리스트도 같은 주소값의 리스트를 가리키고 있었기 때문에 [200,3]으로 변동됨!
- 즉, id(a[1]) == id(c[1]) 라는 것!
- a 값 안의 숫자값은 immutable이기 때문에
import copy
a = [1,2,[3,4]]
b = a # 할당
c = a.copy() # 얕은 복사
d = copy.deepcopy(a) # 깊은 복사
print(id(a)) #12345
print(id(b)) #12345
print(id(c)) #23456
print(id(d)) #34567
a[0] = 100
print(a,id(a),id(a[0])) #100,2,3,4 #12345 #00000
print(b,id(b),id(b[0])) #100,2,3,4 #12345 #00000
print(c,id(c),id(c[0])) #1,2,3,4 #23456 #10000
print(d,id(d),id(d[0])) #1,2,3,4 #34567 #10000
a[2][0] = 300
print(a,id(a),id(a[2][0])) #100,2,300,4 #12345 #20000
print(b,id(b),id(b[2][0])) #100,2,300,4 #12345 #20000
print(c,id(c),id(c[2][0])) #1,2,300,4 #23456 #20000
print(d,id(d),id(d[2][0])) #1,2,3,4 #34567 #30000
- In-place vs Out-of-place
- In-place (제자리 수정)
- 새로운 객체를 만들지 않고, 기존 객체가 저장된 메모리 주소(그 exact 자리)로 찾아가서 내부의 데이터만 슬쩍 바꾸는 방식
- 메모리를 아낄 수 있지만, 원본 데이터가 파괴됨
- ex) list.append(), list.sort(), list[0] = 1
- Out-of-place (외부 생성 / 사본 생성)
- 기존 객체는 건드리지 않고 메모리의 다른 공간(Out)에 새로운 객체를 만들어 그 결과를 담는 방식
- 원본이 안전하게 보존되지만, 새로운 메모리 공간이 필요함
- ex) sorted(list), a = a+1, a = a+[4]
# In-place
def 채널_바꾸기(동생_리모컨):
# 동생이 받은 리모컨으로 리스트의 0번 칸을 고칩니다.
동생_리모컨[0] = "무한도전"
우리집_TV = ["뉴스", "드라마"]
채널_바꾸기(우리집_TV) # 내 리모컨을 복사해서 동생에게 줍니다.
print(우리집_TV)
# 결과: ["무한도전", "드라마"] (원본이 바뀌어버렸어요!)
# Out-of-place
def 새_TV_사주기(동생_리모컨):
# 동생이 "난 이 TV 안 볼래!" 하고 리모컨을 새 리스트에 연결합니다.
동생_리모컨 = ["만화", "스포츠"]
print("동생 방 TV:", 동생_리모컨)
우리집_TV = ["뉴스", "드라마"]
새_TV_사주기(우리집_TV)
print("거실 TV:", 우리집_TV)
# 결과: ["뉴스", "드라마"] (거실 TV는 그대로예요!)
- q=p[:]의 얕은 복사 방식
- 외곽 주소: p와 q는 서로 다른 새 리스트임. 서로 다른 주솟값을 가짐
- 내부 요소:
- p[0]과 q[0]은 숫자 1의 주소를 같이 가리킴
- p[2]와 q[2]는 내부 리스트 [3,4]가 저장된 동일한 주소를 가리킴
x = [1, 2, 3]
y = x[:]
print(id(x),id(y)) # 서로 다른 주소값
x[0] = 99 # x는 주소값 그대로
print(x, y) # [99,2,3],[1,2,3]
print(id(x),id(y)) # 서로 다른 채로 유지
p = [1, 2, [3, 4]]
q = p[:]
print(id(p),id(q)) # 주소값 서로 다름
print(id(p[2]),id(p[2][0])) # but 안의 내용의 주소는 같음
print(id(q[2]),id(q[2][0]))
p[2][0] = 999
print(p, q) # [1,2,[999,4]],[1,2,[999,4]]
print(id(p),id(q)) # 주소값 서로 다른 채로 유지
'Python 공부' 카테고리의 다른 글
| Truthiness (참 같은 값), Truthy/Falsy, 단락평가 (0) | 2026.01.09 |
|---|---|
| 가변 (Mutable) vs 불변 (Immutable) (0) | 2026.01.09 |
| isalpha, isdigit, isinstance 함수 (0) | 2026.01.09 |
| 조건문 / 반복문 활용, 연산자 비교 우선순위, 단락평가 (1) | 2026.01.09 |
| 프로그램 종료 함수 (0) | 2026.01.09 |