파이썬의 참조(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