이상하다
어느 날 코드를 짜던 중 이상하게 오류가 나는 것을 발견했다.
1 |
|
엥? 왜 같은 정수인데.. 이상하다.. 이상해 (파이썬이 거짓말을 하고 있는 것 같다)
과연 진짜 거짓말을 하고 있는 것일까
답은 생각보다 간단한 곳에서(?) 찾을 수 있었다.
1 |
|
간단하게 말하면 -5부터 256 사이에 있는 모든 정수는 미리 선언되어 있고, 이 범위 안에서 사용자가 열심히 선언해도 파이썬은 그냥 선언되어 있는 것을 가져다 쓴다는 개념이다.
그래서 우리는 이런 과정을 통해서 아래와 같은 결과를 얻을 수 있다.
1 |
|
일종의 파이썬은 캐싱을 하고 있는 것이다. 실제 선언하는 것은 쓸데없는 메모리를 많이 사용하게 되니깐..
일단 이렇게 일단락..
안에서는 어떻게 구현되어 있을까?
짓기에는 너무 흥미로웠다. 그냥 내부를 좀 까보고 싶은 생각이 더 컸지만..
그래서 만물의 탄생 근원인 cpython repository 에 가서 해당 함수의 구현부를 검색해 보았다.
(여기 아래에 있는 모든 코드는 해당 링크를 참고하였습니다.)
일단 PyObject의 longobject.c 라는 파일에서 PyLong_FromLong
이라는 함수를 살펴봅시다. (이 친구가 시작이니깐요)
1 |
|
여기 보면 변수 선언하고 일을 하기 전에 CHECK_SMALL_INT
이라는 함수를 호출합니다.
자, 여기서 CHECK_SMALL_INT 이라는 함수(정확히는 매크로 함수입니다)로 가보면 이러한 것을 찾을 수 있습니다.
1 |
|
매개변수로 받은 ival의 값이 -NSMALLNEGINTS보다 크거나 같고, NSMALLPOSINTS보다 작은 경우 get_small_int
함수를 다시 호출합니다.
여기서 NSMALLNEGINTS와 NSMALLPOSINTS는 이미 정의되어 있습니다. 어디에?
1 |
|
즉, 이를 통해서 -5부터 256까지(257은 포함하지 않습니다) 의 값이 들어오게 된다면 get_small_int
라는 함수를 호출하게 됩니다.
그럼.. 이 함수는 뭘 하냐..?
1 |
|
이미 선언되어 있는 small_ints
이라는 배열에 있는 값을 불러와서 반환해주는 아주 심플한 함수입니다.
근데 그럼 이 small_ints
이라는 배열의 값은 언제 세팅될까요..? (찾아보면 됩니다)
1 |
|
바로 _PyLong_Init
이 함수, 초기화할 때 세팅을 뚝딱 해줍니다.
-5부터 256까지 값을 돌면서 object를 initializing 해줍니다. 이를 통해서 우리는 -5부터 256까지의 id값을 가진 small_ints
를 만들게 되는 것이다.
결국, 이를 통해서 정리하자면 아래와 같은 과정을 통해서 이렇게 미리 Integer 값을 caching 하는 것을 알 수 있습니다.
- PyLong 타입이 Initialing될 때 -5 ~ 256까지의 id값을 미리
small_ints
라는 배열에 저장한다. - 만약 새롭게 사용자가 Integer object를 선언하거나 수정된 값이 해당 범위 내라면 새로 할당하는게 아니라 기존에 할당된 id값을 준다.
- 이러면 사람들은 작은 정수의 변수가 선언 되더라도 실제로 새로 할당되지 않으므로 메모리를 넓게 쓸 수 있다(?)
컴퓨터는 거짓말을 하지 않는다
이것은 과학이였다.
컴퓨터는 거짓말을 하지 않았다.
느낀점: 이상하다 싶으면 Reference를 뒤져보자. 그럼 fact가 나올 것이다.