파이썬(Python) 개발자라면 누구나 True, False, None 같은 상수를 당연하게 사용합니다. 하지만 파이썬에 내장된 6가지 '상수'들, 즉 True, False, None, __debug__, Ellipsis(...), NotImplemented가 사실은 제각기 다른 방식으로 구현되어 있다는 점을 아는 사람은 많지 않습니다. 이 글은 이들 상수가 왜 이렇게 복잡하게 작동하는지, 그리고 그 미묘한 차이가 어떤 예상치 못한 결과를 낳는지 분석합니다.
먼저 True, False, None은 키워드(keyword)로 분류됩니다. 이는 일반적인 이름(identifier)처럼 처리되지 않고, 파이썬 코드를 읽는 렉서(lexer) 단계에서 특별한 토큰으로 인식된다는 의미입니다. 이 때문에 `x.True`와 같은 표현은 구문 오류(SyntaxError)를 발생시킵니다. 반면, `__debug__`는 일반 식별자(identifier)이지만, 값을 할당하거나 삭제할 수 없는 유일한 예외적인 특수 식별자입니다. `__debug__ = 67`처럼 할당을 시도하면 `SyntaxError`가 발생하지만, `x.__debug__`처럼 속성으로 접근하려 하면 `AttributeError`가 발생하는 등 키워드와는 또 다른 방식으로 작동합니다. 마지막으로 Ellipsis와 NotImplemented는 단순히 내장(built-in) 객체로, 다른 변수처럼 재할당이 가능합니다. 예를 들어 `NotImplemented = 67`과 같이 값을 변경할 수 있으며, 이는 다른 상수들과 확연히 다른 점입니다.
더 흥미로운 점은 True, False, None, __debug__가 `builtins` 모듈에도 존재한다는 것입니다. `getattr(builtins, 'True')`를 통해 접근하고 심지어 `setattr(builtins, 'True', 67)`로 값을 변경할 수도 있습니다. 하지만 이렇게 `builtins` 모듈에서 값을 변경하더라도, 실제 코드에서 `True`를 사용하면 원래의 불변하는 값(True)이 유지됩니다. 이는 이들 상수가 렉서 단계에서 이미 고정된 값으로 처리되기 때문입니다. 반면 Ellipsis와 NotImplemented는 `builtins` 모듈에서 값을 변경하면 실제 코드의 동작에도 영향을 미칩니다. 다만 Ellipsis의 축약형인 `...`는 여전히 불변하는 Ellipsis 객체를 가리키므로, 이 역시 미묘한 차이를 보입니다.
이러한 파이썬 상수들의 불일치는 언어 설계의 역사적 배경이나 특정 목적을 위한 절충안에서 비롯된 것으로 보입니다. 개발자 입장에서는 이러한 미묘한 차이점을 이해하지 못하면 예상치 못한 버그나 혼란을 겪을 수 있습니다. 특히 `__debug__`의 경우 디버그 모드(-O 옵션)에 따라 값이 달라지므로, 성능 최적화를 위해 특정 코드 블록을 조건부로 실행할 때 유용하게 활용될 수 있습니다. 파이썬이 '명시적인 것이 암시적인 것보다 낫다(Explicit is better than implicit)'는 철학을 가지고 있음에도 불구하고, 이러한 상수들의 불일치성은 파이썬의 '이상한' 면모 중 하나로 남아있습니다.