파이썬의 참조(reference) 방식은 기존에 알고 있던 참조방식과 좀 다르다.
int a = 10;
int &b = a;
b = 7
std::cout << a << std:endl;
위 c++ 코드를 실행하면 7이 출력될 것이다. 포인터 a
와 b
가 참조하는 메모리 공간이 같기 때문이다.하지만 파이썬은 다르다.
a = 10
b = a
print(id(a))
>> 140409867117088
print(id(b))
>> 140409867117088
b = 7
print(id(a))
>> 140409867117088
print(id(b))
>> 140409867117136
b = 7
으로 새로운 값을 할당하면 b
는 a
가 가리키는 메모리 공간을 더 이상 참조하지 않는다. b
는 7이라는 새로운 객체를 참조한다. 정수 7은 불변(immutable) 자료형이기 때문에 참조 변수를 통해 값을 바꿀 수 없기 때문이다.
파이썬에서는 모든 것이 객체다. boolean
, int
, float
등 다른 언어에서 원시 타입(primitive type)으로 분류하는 값들도 객체로 취급한다. 객체는 불변과 가변으로 나뉜다.
따라서 b
가 가리키는 7도 int
클래스의 객체고, b
는 객체가 점유하는 메모리를 가리키는 포인터라고 할 수 있다.
>>> type(b)
<type 'int'>
>>> type(b.__class__)
<type 'type'>
문자/숫자 같은 불변 객체는 변경이 불가능하기 때문에 참조 변수를 통해 조작할 방법이 없으나 리스트, 집합, 딕셔너리와 같은 가변 객체는 참조 변수를 통해 객체를 변경할 수 있다. 하지만 가변 객체의 경우에도 =
연산자로 다른 객체를 참조 변수에 할당하면 다른 ID를 갖게 된다.
>>> a = [1, 2, 3]
>>> b = a
>>> id(a)
4522271376
>>> id(b)
4522271376
>>> b[1] = 3
>>> b
[1, 3, 3]
>>> a
[1, 3, 3]
>>> b = [4, 5, 6]
>>> id(b)
4522271520
s
와 s[:]
의 차이
어떤 리스트 s
가 있다고 하자. id(s)
와 id(s[:])
는 서로 다르다. s[:]
는 s
를 deep copy 한 것이기 때문이다.
>>> s = [1, 2, 3]
>>> id(s)
4522271592
>>> id(s[:])
4522109408
반면에 s[:]
와 s[::-1]
은 리스트의 인덱스들이 참조하는 메모리주소의 순서가 서로 반대일 뿐 동일한 메모리 공간을 참조한다. (리스트 내 각 원소는 연속된 공간에 있다.)
>>> id(s[::-1])
4522109408
>>> s2 = s[::-1]
>>> id(s2[-1])
140409867117256
>>> id(s[0])
140409867117256
>>> id(s2[0])
140409867117304
>>> id(s[-1])
140409867117304
s[::-1]
은 s
를 deep copy 한 것이므로 s = s[::-1]
은 s
가 참조하던 메모리 공간을 더 이상 참조하지 않고 s[::-1]
이 가리키는 공간을 참조하도록 한다.
>>> s = [1, 2, 3]
>>> id(s)
4522109408
>>> id(s[::-1])
4522271592
>>> s = s[::-1]
>>> id(s)
4522271592
반면에 s[:] = s[::-1]
은 s[:]
의 각 인덱스가 가리키는 주소를 바꾼다.
>>> s2 = s[:]
>>> s3 = s[::-1]
>>> id(s2[0])
140409867117304
>>> id(s3[0])
140409867117256
>>> s2 = s3
>>> id(s2[0])
140409867117256