Python Thinks Different: What Actually Happens Inside Your Code (Visual Guide)
파이썬은 다르게 동작합니다: 코드 안에서 실제로 일어나는 일
파이썬 변수는 값을 담는 상자가 아니라 객체에 붙는 이름이며, 대입은 이름을 객체에 연결합니다. 글은 가변성, 기본 인수, 복사, 참조 카운팅과 순환 참조 수거 등 객체 모델을 코드 예제로 설명합니다.
- 주제
AI 요약
파이썬에서 x = 10을 실행하면 변수라는 상자에 정수를 넣는 게 아니라 정수 객체에 x라는 이름을 연결합니다. 따라서 y = x는 값을 복사하지 않고 같은 객체에 이름을 하나 더 붙입니다. 글은 이 관점을 출발점으로 객체의 정체성, 가변성과 불변성, 메모리 관리가 실제 코드에서 어떤 차이를 만드는지 예제와 함께 설명합니다.
이름은 객체를 가리킵니다
x = 42에서 x는 정수 객체를 가리키는 이름입니다. 함수와 클래스, 모듈도 객체이며, type(type)의 결과가 type이라는 예로 파이썬의 타입 체계를 소개합니다. 불변 객체에 값을 더하면 기존 객체를 바꾸는 대신 새 객체를 만들고 이름을 새 객체에 다시 연결합니다. 반면 리스트 같은 가변 객체는 제자리에서 바꿀 수 있습니다. b = a로 같은 리스트를 가리킨 뒤 b.append(4)를 실행하면 a로도 바뀐 내용을 확인합니다.
가변 기본 인수와 함수 인자
글은 함수의 기본 인수로 리스트를 지정하면 리스트가 함수 정의 시 한 번 만들어져 호출 사이에 공유된다고 설명합니다. 그래서 items=[]를 기본값으로 두고 append하면 두 번째 호출에도 첫 번째 호출에서 추가한 항목이 남습니다. 기본값을 None으로 두고 함수 안에서 새 리스트를 만들면 이 문제를 피할 수 있습니다.
함수에 리스트를 전달할 때도 리스트 자체를 복사하지 않습니다. 매개변수 이름은 전달된 객체를 가리킵니다. 그 이름을 통해 리스트를 수정하면 호출한 쪽에서도 변경을 보지만, 함수 안에서 매개변수에 새 리스트를 대입하면 지역 이름만 다른 객체를 가리킵니다. 글은 이를 pass by object reference 또는 pass by assignment라고 부릅니다.
참조와 객체 정체성
글은 CPython의 참조 카운팅과 순환 참조 수거를 설명합니다. 객체를 가리키는 참조 수가 0이 되면 참조 카운팅으로 객체를 정리합니다. 다만 서로를 가리키는 두 객체는 바깥에서 더는 접근하지 못해도 참조 수가 0이 되지 않을 수 있습니다. 이 경우 순환 참조를 찾는 가비지 컬렉터가 정리를 맡습니다. 본문은 sys.getrefcount()와 gc.get_threshold()를 이용한 확인 예제도 제시합니다.
==는 값이 같은지 비교하고 is는 같은 객체인지 비교합니다. 따라서 값 비교에는 ==를 쓰고, 객체 정체성을 확인할 때만 is를 써야 합니다. 글은 작은 정수 캐싱과 문자열 인터닝을 예로 들며 is 결과가 값 비교의 근거가 될 수 없다고 강조합니다. 다만 정수 캐싱 범위와 문자열 인터닝은 파이썬 언어 전체가 보장하는 규칙이 아니라 구현이나 실행 맥락에 따라 달라질 수 있습니다. id() 역시 객체의 정체성을 나타내며, 실제 메모리 주소를 뜻하는지는 구현에 따라 다릅니다.
얕은 복사와 깊은 복사
중첩 리스트에 변수 이름만 더 붙이면 원본과 별칭이 같은 객체를 가리킵니다. copy.copy()는 바깥 리스트만 새로 만들고 안쪽 리스트는 공유하므로, 내부 요소를 바꾸면 원본도 바뀝니다. copy.deepcopy()는 중첩된 객체까지 복사해 서로 독립된 구조를 만듭니다. 글은 데이터 구조의 공유가 필요한지에 따라 별칭, 얕은 복사, 깊은 복사 중 무엇을 쓸지 구분합니다.
`__slots__`와 메모리
일반 인스턴스는 보통 속성을 __dict__에 저장합니다. 글은 속성을 미리 정할 수 있는 클래스에서 __slots__를 선언하면 인스턴스별 딕셔너리를 피할 수 있다고 설명합니다. 예제에서는 sys.getsizeof()로 딕셔너리와 슬롯 사용 객체의 크기를 비교하고, 백만 개 객체를 만드는 상황에서 tracemalloc으로 메모리 사용량을 비교합니다. 본문은 슬롯 버전이 대략 40~50% 적은 메모리를 쓴다고 제시합니다. 실제 차이는 파이썬 구현과 객체 구성에 따라 달라질 수 있습니다.
dev.to 반응
- @examadda — 훌륭한 글입니다! 이름표와 상자의 차이, 파이썬의 참조 카운팅 설명은 깔끔하고 버그 없는 코드를 작성하는 데 큰 도움이 됩니다. 시각적으로 잘 설명했습니다. 저희 커뮤니티에서도 이런 심층 학습 자료를 공유하는 걸 좋아합니다. 파이썬 기초 개념을 더 연습하고 익히고 싶다면 ExamAdda 학습 모듈을 확인해 보세요.
- @kyisaiah47 — 순환 참조 수거기는 접근할 수 없는 순환 구조와, 순환을 포함하지만 여전히 접근할 수 있는 객체 그래프를 어떻게 구분하나요?
- @henry786 — 얕은 복사와 깊은 복사의 차이는 저희 The Printing World 업무에도 아주 관련이 있습니다. 복잡하게 중첩된 상자 치수, 인쇄 레이어, 다이라인을 다루기 때문에 주문 스크립트에서 참조를 실수로 복사하면 실제 생산에 큰 문제가 생길 수 있습니다.
원문: dev.to / 번역·요약: Trawling