Python은 값을 할당하지 않는다?
파이썬은 메모리 상에 할당한 값을 직접 다루지 않습니다. 모든 값은 객체로써 활용됩니다.

a = 10
위의 코드를 통해 생성 - 연결이라는 두 가지 동작이 진행됩니다.
- 10 이라는 값이 객체로써 메모리에 생성됩니다.
- a 라는 변수에 10 값이 있는 메모리 주소에 대한 참조를 할당합니다.
- 사실 엄밀히 말하면 -5~256까지의 숫자는 파이썬의 기본 메모리에 캐싱된 객체라, 조금 이야기가 다르지만, 넘어가도록 하겠습니다!
a = 10
a = 20
위의 코드에서는 “a” 에 10을 할당한 후, 다시 20이란 값을 새롭게 할당합니다.
- 10 값이 메모리에 생성됩니다.
- a 변수에 10의 메모리 주소 참조를 할당합니다.
- 20 값이 메모리에 생성됩니다.
- a 는 10에 대한 메모리 주소 참조를 해제하고, 새롭게 20에 대한 메모리 주소 참조를 할당합니다.
- 10 은 사용되는 곳이 없으므로 (참조가 모두 해제되었으므로) 가비지 컬렉션의 사이클에 맞춰 메모리 상에서 값이 삭제됩니다.
파이썬에서는 모든 것이 객체(Object)이기 때문에, 참조형 밖에 없습니다.
primitive (자바의 int, char와 같은 원시 타입) 타입이 존재하지 않으므로, 변수에 값을 직접할당하지 않습니다.
즉, 모든 변수는 로우한 값이 아닌, 값을 가진 객체에 대한 참조를 저장합니다.
(이것이 파이썬의 낮은 성능에 한 몫합니다.)
x = 2000
y = 2000
print(id(x)) # 추상화된 메모리 공간 주소를 반환하는 함수
print(id(y))
>>> 4376423984
>>> 4376423984
만약 C나 Java처럼 원시 타입이 있는 언어일 경우,
두 변수에 같은 값을 할당하더라도 값을 직접 저장하므로, x와 y는 서로 다른 메모리 주소를 갖고 있을 수 있습니다.
불변 객체 (int, str, tuple)
일반적으로 한번 생성된 후 값이 변경될 수 없는 불변 객체인 int, str, tuple 같은 경우를 먼저 짚고 넘어가야 합니다.
위의 경우에는, 언뜻보면 x에 할당된 int 객체를 수정하는 것으로 보일 수 있지만, 엄밀히 말하면 아닙니다!
- 10 정수 값을 메모리 공간에서 객체로 생성합니다.
- x 에 10 값을 가진 int 객체의 메모리에 대한 참조를 할당합니다.
- x 객체의 값인 10을 가져와, 다시 5를 더하고 새로운 메모리 공간에 할당합니다.
- x 는 10 값 객체에 대한 참조를 해제합니다.
- x 에 방금 생성한 새로운 15 값을 가진 int 객체에 대한 메모리 참조를 할당합니다.
- 위에서 예를 들었다시피 메모리 공간에 위치한 객체 자체를 수정하는 것이 아니라,
새로운 객체를 만들고 이에 대한 참조를 새롭게 할당하는 것입니다!
가변 객체 (List, Set, Dict)
그러나, 가변 객체에서는 메모리에 할당된 객체에 대한 수정이 가능합니다.
대체로 문제는 여기서 발생합니다.
arr1 = [[1,2], [3,4]]
arr2 = arr1.copy()
# 안쪽에 있는 list는 메모리 참조
# 2차원 이상의 리스트를 copy 하고 싶다면 deep copy를 해줘야함
arr2에는 arr1에 대한 참조를 저장하고, arr1에 값을 추가했는데, arr2도 변경되었습니다?!
값이 아닌 참조를 할당했기 때문입니다.
또한, 가변 객체는 내부적인 값을 직접적으로 수정할 수 있습니다.
arr1 변수를 통해 수정한 사항이 반영되면, 두 변수 arr1, arr2 모두 하나의 메모리 주소 공간에 할당된 객체를 바라보게 되므로 수정 사항이 반영된 객체를 보게 됩니다.
그렇기 때문에 별 생각 없이 코드를 짜게 되면 다음과 같은 문제를 마주치기도 합니다.
arr = [0, 0, 0, 0, 0]
visit = arr * 5
visit[0][0] = 1
print(visit)
>>> [[1, 0, 0, 0, 0]
>>> [1, 0, 0, 0, 0]
>>> [1, 0, 0, 0, 0]
>>> [1, 0, 0, 0, 0]
>>> [1, 0, 0, 0, 0]]
분명 첫 번째 행의 첫 번째 열만 바꿨는데, 모든 리스트의 첫 번째 열 값이 바뀌었습니다.
다섯 개의 리스트 모두 사실상 하나의 객체를 공유(arr에 할당된 하나의 메모리를 참조)하고 있는 중입니다.
이를 방지하기 위해선, 당연히 2차원 리스트를 모두 별도의 객체로 생성하여 독립된 메모리 공간을 할당해주어야 합니다.
visit = [[0] * 5 for _ in range(5)]
visit 변수에 할당된 값은, arr에 대한 할당을 똑같이 다섯 개 연달아 넣은 것이기 때문입니다.