파이썬의 비밀스러운 삶: 메모리 속의 동상
Source: Dev.to
문자열로 별칭(alias) 보여주기
도서관 공기 속에 떠다니는 먼지 입자를 가르는 오후 햇살이 비쳤다. Timothy는 Name Tag 개념을 마스터하고 자신감이 넘쳤다. 그는 Margaret에게 배운 것을 보여주고 싶었다.
“이거 봐,” Timothy가 노트북을 그녀에게 향해 돌리며 말했다. “
greeting_one과greeting_two두 개의 태그를 만들 거야. 두 태그는 같은 문자열 객체를 가리키고 있어. 그럼 하나를 수정하고 다른 하나도 바뀌는 걸 증명해볼게.”
greeting_one = "Hello"
greeting_two = greeting_one
# Timothy's plan: modify the object via the second tag.
greeting_two += " World"
print(f"greeting_two is: {greeting_two}")
print(f"greeting_one is: {greeting_one}")
Timothy는 두 변수 모두 "Hello World"를 출력할 것이라고 기대했다.
greeting_two is: Hello World
greeting_one is: Hello
그는 코드를 다시 실행했지만 결과는 동일했다.
“하지만 너는 그게 레퍼런스라고 했잖아! 리스트로 보여줬잖아!”
Margaret는 문자열이 가변 컨테이너와는 다르게 동작한다고 설명했다.
문자열 불변성 이해하기
Margaret는 대리석 조각상을 비유로 사용했다:
- 돌 (불변) – 문자열은 제자리에서 변경될 수 없으며, 마치 돌 조각상과 같다.
- 화이트보드 (가변) – 리스트는 새로운 객체를 만들지 않고도 변경될 수 있다.
그녀는 과정을 다음과 같이 설명했다:
- 파이썬은
"Hello"라는 라벨이 붙은 원래의 돌 블록을 본다. greeting_two += " World"연산은 기존 돌에" World"를 새기는 것이 불가능하다.- 파이썬은 새로운 메모리 위치에
"Hello World"를 담은 새로운 돌 블록을 만든다. greeting_two는 이 새로운 블록에 다시 바인딩되고,greeting_one은 여전히 원래의"Hello"블록에 붙어 있다.
따라서 += 연산 이후 두 태그는 더 이상 같은 객체를 가리키지 않게 된다.
객체 ID 확인하기
Timothy는 새로운 객체가 생성됐다는 구체적인 증거를 원했다.
s = "Hello"
print(f"Start ID: {id(s)}")
s += " World"
print(f"End ID: {id(s)}")
예시 출력:
Start ID: 2667049832100
End ID: 2667051284300
다른 ID가 나타나는 것은 새 문자열 객체가 할당됐음을 확인시켜준다.
불변 vs 가변 타입
| 카테고리 | 예시 | 특징 |
|---|---|---|
| 불변 (돌) | str, int, float, tuple | 제자리에서 변경될 수 없다. +=, upper(), 슬라이스와 같은 연산은 새 객체를 만든다(재바인딩). 참조를 공유해도 안전하다. |
| 가변 (화이트보드) | list, dict, set | 제자리에서 수정될 수 있다. 연산이 원본 객체를 변경하므로, 공유된 참조도 변경을 확인한다. |
요약
- 문자열(및 기타 불변 타입)은 절대 변경되지 않는다; 모든 “수정”은 새 객체를 만든다.
- 리스트와 기타 가변 컨테이너는 제자리에서 변경될 수 있어, 별칭도 동일한 업데이트를 본다.
- 어떤 객체가 불변이고 가변인지 이해하면, 참조를 공유할 때 예상치 못한 버그를 방지할 수 있다.
다음 번에 Margaret와 Timothy는 “진리의 비밀스러운 삶”을 탐구할 것이다 — 파이썬에서 심지어 빈 값도 False가 될 수 있음을 발견한다.