카테고리 없음

파이썬의 기이한 특징들

Jueuunn7 2025. 3. 12. 20:45

1. 256 is 256은 참, 257 is 257은 거짓

파이썬의 비교 연산자에는 ==와 is가 있습니다.

==는 두 객체가 같은 값을 갖고 있는지를 비교하지만 is 연산자는 두 객체의 id값이 동일한 값인지를 검사합니다.

id는 파이썬의 객체를 구별하는 값이라고 생각하면 됩니다.

>>> a = 10
>>> b = 10.0
>>> a == b
True
>>> a is b
False

10과 10.0의 값은 같지만 서로 다른 객체이기 때문에 is 비교연산자로 서로의 id값을 비교하게 되면 False가 됩니다.

>>> (id(a), id(b))
(4343095344, 4344870928)

 

파이썬은 새로운 정수 객체를 만들어서 메모리에 올리는 시간이 짧은 편입니다.

그 이유는 CPython 인터프리터에서 최적화를 위해 -5 ~ 256까지의 정수를 미리 메모리에 로드해 두기 때문입니다. 이런 정수들은 preallocated 정수라고 부릅니다.

예시로, 프로그램에서 1422 같은 숫자보단 0, 1, 2 같은 숫자가 더 많이 사용되기 때문에 CPython은 흔히 사용되는 정수들을 자동으로 생성해 둡니다. 개발자가 새로운 정수 객체를 만드려고 하면 -5 ~ 256 사이에 있는 정수인지를 확인하고, 해당 범위 안에 포함된다면 새로운 정수 객체를 만들지 않고 기존의 객체를 반환하는 식으로 동작합니다. 이 동작으로 몇몇 숫자들을 중복저장하지 않기 때문에 메모리를 줄일 수 있습니다.

하지만 이런 최적화 때문에 예상치 못한 동작이 일어날 수 있습니다.

>>> a = 256
>>> b = 256
>>> a is b
True
>>> c = 257
>>> d = 257
>>> c is d
False

a와 b 변수는 각각 256 값을 갖고 있습니다. 256은 preallocated 정수이기 때문에 서로 같은 객체를 참조하고 있습니다. 그래서 is 비교연산 시에 True를 반환합니다.

하지만 c와 d는 257이라는 정수 객체를 각각 별도로 만들고 참조하기 때문에 is 비교연산시에 False를 반환합니다.

하지만 257 is 257은 또 True를 반환합니다.

>>> 257 is 257
True

257 is 257이 True로 평가되는 이유는 CPython에선 하나의 문 안에서 사용되는 값이 다시 사용되게 된다면 똑같은 객체를 반환하게 됩니다. 그래서 257 is 257은 True가 반환됩니다.

이로써 파이썬에서는 각 참조마다 객체를 새로 만들지 않고 왼쪽 그림처럼 객체를 하나 생성하고 여러 참조를 연결하는 방법을 사용한다는 걸 알 수 있습니다. 자바의 스트링풀과 비슷합니다.

 

2. 문자열 인터닝

또한 파이썬은 동일한 문자열에 대한 새로운 복사본을 만들지 않고 객체를 재사용합니다.

>>> a = 'hello'
>>> b = 'hello'
>>> a is b
True
>>> id(a), id(b)
(4346251472, 4346251472)

변수 b를 생성할 때 a 문자열과 같다는 걸 인식하고 a의 참조를 b에도 할당해 줍니다.

이와 같은 구조를 문자열 인터닝이라고 부릅니다.

하지만 모든 상황에서 완벽하게 작동하지는 않습니다.

>>> a = 'abc'
>>> b = 'a'
>>> b += 'bc'
>>> a is b
False
>>> id(a), id(b)
(4342637608, 4346165744)

이번엔 b 변수에 a를 먼저 할당해 두고, 나중에 bc를 추가하였습니다. 최종적으로 a, b 둘 다 abc라는 문자열을 갖게 되었지만, is로 비교하였을 때 False가 나오고, 서로의 id 값도 다른 걸 볼 수 있습니다.

모든 최적화가 가능한 문자열들을 다 탐색하지는 않습니다. 가끔 최적화를 하는 것보다 최적화 과정에서 걸리는 시간이 더 높은 경우도 있기 때문입니다. 이러한 이유로 자체 내부 구현에 대해 의존하는 코드는 발생하면 안 됩니다.

 

3. type(bool) = int

부동소수점 10.0이 10과 동일하게 취급되는 것처럼 True와 False도 1과 0으로 취급됩니다.

1과 0을 bool처럼 쓸 수 있는 걸 넘어서 bool값은 int 타입의 하위 클래스로 구현되어 있습니다.

>>> bool(1)
True
>>> bool(0)
False

isinstance로 직접 타입을 비교해서 정수형인지도 확인이 가능합니다.

>>> isinstance(True, bool)
True
>>> isinstance(True, int)
True

True, False는 bool 타입의 값이지만 int의 하위 클래스로 구현되었기 때문에 정수로도 사용할 수 있습니다.

>>> True + True + False  # 1 + 1 - 1
2
>>> -True  # -(1)
-1
>>> 'abcd'[True]  # 'abcd'[1]
b

파이썬 3 이후부터는 True False가 키워드로 변경되었다. 물론 이전엔 키워드가 아니었기 때문에 True, False라는 변수가 가능했습니다.

>>> True is False
>>> False = True
>>> True is False
True