* 바쁘신 분들을 위한 3줄 요약
1. mutable이란, "메모리 주소를 유지한 채 값을 변경할 수 있다"란 뜻이다.
2. list, dict 등은 변경 가능(mutable)하며
3. int, float, string, tuple 등은 변경 불가능(immutable)하다.
Mutable, Immutable은 무슨 뜻일까?
mutable은 변경가능이란 뜻이며 immutable은 변경 불가능이란 뜻입니다.
python에서 변경 가능(mutable)한 Data Type으로는 list, dict 등이 있으며,
변경 불가능(immutable)한 Data Type으로는 int, float, string, tuple 등이 있습니다.
하지만 이 정도의 설명으로는 오해를 하기 쉽습니다. int는 정말로 변경이 불가능한가요?
>>> a = 1000
>>> a = 2000
>>> print(a)
2000 ## 값이 바뀜
위의 코드에서는 a에 할당된 int 값을 변경 했습니다. 어떻게 된 일일까요? 이는 "변경"의 대상이 a변수의 값이라 생각했기 때문입니다. immutable의 특성은 메모리 주소의 값 관점에서 생각해야 올바른 의미를 알 수 있습니다.
>>> a = 1000
>>> hex(id(a))
0x1bfe0932 ## a의 주소
>>> a += 2000
>>> hex(id(a))
0x1bce2be3 ## 주소가 바뀜
값을 변경하니 주소값이 바뀝니다. 메모리 주소에 할당된 int객체는 값을 바꿀 수는 없습니다. (immutable)
그렇다면 변경이 가능(mutable) 한 list는 어떨까요?
>>> a = [1, 2, 3]
>>> hex(id(a))
0x2cba8301
>>> a = [1, 2, 3, 4]
>>> hex(id(a))
0x2cbce103 ## 주소가 바뀜
이게 어찌된 일일까요? list의 값을 바꿨더니 주소가 변했습니다. 이는 "=" 연산자가 a를 새로운 주소에 할당했기 때문입니다. mutable한 성질을 확인하려면, "=" 연산자를 통해 새로운 할당이 아닌, 그 객체 자체의 값을 변경해야 합니다.
Mutable 예제
>>> a = [1, 2, 3, 4]
>>> hex(id(a))
0x3daac14642f
>>> a[0] = 5
>>> a
[5, 2, 3, 4] ## 첫번째 값이 바뀜
>>> hex(id(a))
0x3daac14642f ## 주소가 유지됨
list의 원소를 바꾸었음에도 메모리 주소값이 유지됩니다.(mutable)
주소가 유지되면 어떤 특징이 생길까요? 바로 같은 주소를 공유하는 변수들을 한 번에 컨트롤 할 수 있게 됩니다.
아래는 a와 b에 같은 주소를 할당 한 예제입니다.
>>> a = [0]
>>> b = a ## 같은 주소를 갖도록 지정
>>> hex(id(a))
0xabfe2757
>>> hex(id(b))
0xabfe2757 ## a와 b가 서로 같은 주소를 가진다.
>>> a += [1] ## 혹은 .append method를 사용해도 된다.
>>> print(a)
[0, 1] ## a의 값을 변경하면
>>> print(b)
[0, 1] ## b까지 변경이 되었음을 알 수 있다.
>>> hex(id(b))
0xabfe2757 ## b의 주소는 유지.
단, a += [1]이 된다고 해서 a = a + [1]로 하면 안됩니다. "=" 연산자는 새로운 변수를 생성하여 새로운 주소를 할당합니다.
>>> a = [0]
>>> b = a ## 같은 변수로 지정
>>> hex(id(a))
0xabfe2757
>>> hex(id(b))
0xabfe2757 ## a와 b가 서로 같은 주소를 가진다.
>>> a = a + [1] ## a를 새로 생성하였다.
>>> hex(id(a))
0xabf21594 ## a는 이제 새로운 주소에 저장이 되었다.
>>> print(a)
[0, 1] ## a의 값을 변경해도
>>> print(b)
[0] ## b의 값은 변경되지 않는다.
>>> hex(id(b))
0xabfe2757 ## b의 주소는 유지.
이처럼 immutable과 mutable의 특성을 잘 이해한다면, list로 다차원 배열을 어떻게 구현해야 할 지 알 수 있습니다.
아래는 누구나 한번쯤은 실수해보는 2차원 list 선언 예제입니다.
>>> a = [0] * 10 ## immutable한 0을 복사해서 1차원 list을 선언.
>>> print(a) ## a는 다음과 같다.
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> a[0] = 1 ## list 내의 값을 변경한다.
>>> print(a) ## 값이 의도대로 변경되었다.
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> a = [[0] * 10] * 10 ## mutable한 list를 복사해서 2차원 list를 선언
>>> print(a) ## 정상 같지만, 안쪽 list들은 전부 같은 주소를 가리킨다.
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
>>> a[0][0] = 1 ## 2차원 배열된 list의 (0, 0)을 바꿔보았다.
>>> print(a) ## 안쪽 list들의 첫번째 값이 전부 바뀌었다.
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
>>> hex(id(a[0]))
0x12cba3059 ## 첫번째 열의 주소.
>>> hex(id(a[1]))
0x12cba3059 ## 첫번째 열과 두번째 열의 주소가 같다.
list 2차원 배열은 아래 코드와 같이 선언해야 합니다. for문을 통해 각각의 list를 전부 다른 객체로 선언했습니다.
a = [[0 for x in range(10)] for y in range(10)]
* reference
https://docs.python.org/3/library/stdtypes.html#immutable-sequence-types
https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
'Python > Basic' 카테고리의 다른 글
Python - List Comprehension (0) | 2023.03.19 |
---|---|
Python - Int의 크기가 28bytes인 이유 (5) | 2023.03.01 |
Python - 기본 문법정리 (0) | 2023.02.24 |
Python - List는 어떻게 데이터를 관리하는가? (0) | 2023.02.15 |
Python - is와 ==의 차이 (1) | 2023.02.10 |