
최근에 파이썬 수업을 하기 위해 기본 개념을 다시 보던 중 뮤터블과 이뮤터블의 개념이 반복해서 나왔다. 해당 개념에 대해 학생들에게 더 쉽게 설명하기위해 여러 자료들을 참고하여 정리해보려고 한다.
뮤터블과 이뮤터블을 설명하기 전에 먼저 파이썬에서 변수 선언 방식을 알아보자.
파이썬에서 변수는 객체를 가리킨다. 파이썬에서는 변수의 타입을 지정해줄 필요가 없기 때문에 다음과 같이 변수를 선언할 수 있다.
num = 10
print(id(num)) # 4362823392
참고: id()는 객체의 주소값을 반환하는 함수이다.
이해를 돕기 위해 변수가 만들어지는 과정을 다음과 같이 간단히 정리할 수 있다.
1) 10이라는 객체가 만들어진다.
2) num이라는 변수가 생성된다.
3) num이 10을 가리킨다.

컴퓨터 메모리에 10이라는 값이 저장되고, num은 10이 저장된 메모리의 위치를 가리킨다.
10이라는 정수형 객체를 num이라는 변수가 가리키고 있는 것이다.
파이썬 공식 문서에는 뮤터블과 이뮤터블을 다음과 같이 설명하고 있다.
id()는 일정하게 유지한다.
- Mutable objects can change their value but keep their id().
- Immutable
: An object with a fixed value. Immutable objects include numbers, strings and tuples. Such an object cannot be altered. A new object has to be created if a different value has to be stored. They play an important role in places where a constant hash value is needed, for example as a key in a dictionary.
출처: https://docs.python.org/3/glossary.html#term-immutable
이뮤터블 객체인 숫자형, 문자열과 뮤터블 객체인 리스트를 코드와 그림을 통해 비교해보자.
a = 1
b = a
print(id(a)) # 4362823104
print(id(b)) # 4362823104
b += 4
print(a) # 1
print(b) # 5
print(id(a)) # 4362823104
print(id(b)) # 4362823232

b의 값을 바꾸게 되면 b는 a가 가리키는 주소(4362823104)를 더이상 가리키지 않고 다른 메모리 주소를 가리키게된다. ([그림1] -> [그림2])
a = 'qwe123'
b = a
print(id(a)) # 4353446256
print(id(b)) # 4353446256
b += 't'
print(a) # qwe123
print(b) # qwe123t
print(id(a)) # 4353446256
print(id(b)) # 4353044976
문자열 자료형도 숫자형과 마찬가지로 이뮤터블(immutable)자료형이므로 새 값을 만들기 위해 새 객체가 생성되어 b가 가리키는 메모리 주소가 변경된다. ([그림3] -> [그림4])

a = [1, 2, 3]
b = a
print(a) # [1, 2, 3]
print(b) # [1, 2, 3]
print(id(a)) # 4348776128
print(id(b)) # 4348776128
b.append(4)
print(a) # [1, 2, 3, 4]
print(b) # [1, 2, 3, 4]
print(id(a)) # 4348776128
print(id(b)) # 4348776128
리스트는 값이 변경될 수 있는 자료형이므로 [1, 2, 3]을 담고있던 메모리의 값 자체가 [1, 2, 3, 4]로 변경된다. 주소는 당연히 그대로 유지된다.

a = (1, 2, 3)
b = a
print(a) # (1, 2, 3)
print(b) # (1, 2, 3)
print(id(a)) # 4348773824
print(id(b)) # 4348773824
b += (4,)
print(a) # (1, 2, 3)
print(b) # (1, 2, 3, 4)
print(id(a)) # 4348773824
print(id(b)) # 4353366416
튜플은 숫자형과 문자열 자료형과 마찬가지로 값이 변경될 수 없기 때문에 b += (4,) 코드 실행 이후 새로운 객체가 만들어진다.

딕셔너리는 변경이 가능한 뮤터블 객체이므로 리스트와 유사하게 동작한다. 그럼 변경 불가능한 객체에서는 항상 같은 객체를 공유할 수 밖에 없을까?
그렇지는 않다. 딕셔너리와 같은 뮤터블 객체에서 같은 객체를 공유하지 않도록 리스트의 [:]나 deepcopy함수를 사용할 수도 있다.
# copy모듈 import
import copy
a = [1, 2, 3]
b = a
# a와 c가 같은 객체를 공유하지 않는다. (리스트만 가능)
c = a[:]
# deep copy를 이용하면 a와 deepCpy가 같은 객체를 공유하지 않는다.
deepCpy = copy.deepcopy(a)
print(id(a)) # 4323077376
print(id(b)) # 4323077376
print(id(c)) # 4323241920
print(id(deepCpy)) # 4315780416
immutable, mutable 속성은 변수가 함수의 매개변수로 전달될 때 원래 입력 변수 값이 변경되는지 그렇지 않은지를 결정하기 때문에 중요한 개념이다. 이번 기회를 통해 다시 한 번 정리할 수 있었다. 다음 시간에는 Call-By-Value와 Call-By-Reference의 개념에 대해 정리할 예정이다.
혹시나 잘못된 내용이 있다면 언제든지 댓글로 알려주세요!