PEP 285 – bool 타입 추가
- Author:
- Guido van Rossum <guido at python.org>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 08-Mar-2002
- Python-Version:
- 2.3
- Post-History:
- 08-Mar-2002, 30-Mar-2002, 03-Apr-2002
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
개요
이 PEP는 False와 True라는 두 상수를 가진 새로운 내장 타입 bool의 도입을 제안합니다. bool 타입은 (C에서) int 타입의 단순한 서브타입이 될 것이며, False와 True 값은 repr()과 str()을 제외한 대부분의 측면에서 0과 1처럼 동작할 것입니다(예를 들어, False==0과 True==1은 참이 될 것입니다). 개념적으로 불리언 결과를 반환하는 모든 내장 연산은 0이나 1 대신 False나 True를 반환하도록 변경될 것입니다. 예를 들어, 비교 연산, “not” 연산자, isinstance()와 같은 프레디케이트가 있습니다.
검토
저는 평생 쓸 만큼 충분한 피드백을 모았으므로, 검토 기간이 공식적으로 종료되었음을 선언합니다. 오늘 중국 음식을 먹었는데, 포춘 쿠키에는 “강하고 신랄한 말은 약한 주장을 나타낸다”라고 적혀 있었습니다. 이는 이 PEP에 반대하는 몇몇 게시물들을 떠올리게 했습니다… :-)
어쨌든, 다음은 저의 BDFL 선언입니다. (요약: 저는 아무것도 바꾸지 않을 것이며, 모든 변형안은 거부됩니다.)
- 이 PEP를 수용해야 합니까?
=> 예.
이 PEP에 반대하는 주장이 많았습니다. 그중 다수는 오해에 근거한 것이었습니다. 저는 아래 PEP 본문에서 가장 흔한 오해 몇 가지를 명확히 하려고 노력했습니다. 제게 조금이라도 무게감 있는 유일한 문제는, “if x”만으로 충분한 곳에 초심자들이 “if x == True”라고 쓰는 경향입니다. 이에 대해서는 아래에서 더 다룹니다. 저는 이것이 이 PEP를 거부할 충분한 이유는 아니라고 생각합니다.
str(True)는 “True”를 반환해야 합니까, “1”을 반환해야 합니까? “1”은 하위 호환성 문제를 줄일 수 있지만, 이상해 보입니다. (repr(True)는 항상 “True”를 반환할 것입니다.)=> “True”.
거의 모든 검토자가 이에 동의합니다.
- 상수를 (None과 유사하게) ‘True’와 ‘False’라고 불러야 합니까, 아니면 (C++, Java, C99에서처럼) ‘true’와 ‘false’라고 불러야 합니까?
=> True와 False.
대다수 검토자는 다른 언어와의 일관성보다 파이썬 내부의 일관성이 더 중요하다는 데 동의합니다.
- 예를 들어 True+1이 (파이썬 3000에서) 결국 허용되지 않도록, 적절한 경고를 통해 향후 bool에 대한 비불리언 연산을 없애기 위해 노력해야 합니까?
=> 아니오.
산술 연산을 전혀 지원하지 않는 “교과서적인” bool을 선호하는 소수의 목소리 큰 사람들이 있지만, 대다수 검토자는 bool이 언제나 산술 연산을 허용해야 한다는 데 저와 동의합니다.
operator.truth(x)는 int를 반환해야 합니까, bool을 반환해야 합니까?=> bool.
팀 피터스는 이것이 int를 반환해야 한다고 믿지만, 다른 거의 모든 검토자는 bool을 반환해야 한다는 데 동의합니다. 제 근거는 다음과 같습니다:
operator.truth()는 인자에 불리언 컨텍스트를 강제하기 위해 존재합니다(이는 C API인PyObject_IsTrue()를 호출합니다). 결과가 int로 보고되는지 bool로 보고되는지는 부차적인 문제입니다. bool이 존재한다면 그것을 사용하지 않을 이유가 없습니다. (이 PEP에 따라operator.truth()는 이제bool()의 별칭이 되는데, 이는 괜찮습니다.)- bool은 int를 상속해야 합니까?
=> 예.
이상적인 세계에서라면 bool은 혼합 모드 산술을 수행할 줄 아는 별도의 정수 타입으로 구현하는 편이 더 나았을지도 모릅니다. 하지만 bool을 int로부터 상속받게 하면 구현이 엄청나게 쉬워집니다(
PyInt_Check()를 호출하는 모든 C 코드가 계속 동작한다는 점도 그 이유 중 하나입니다 – 이 함수는 int의 서브클래스에 대해서도 참을 반환합니다). 또한, 저는 이것이 대체 가능성(substitutability) 측면에서도 옳다고 믿습니다: int를 요구하는 코드는 bool을 넘겨받아도 0이나 1과 동일하게 동작합니다. bool을 요구하는 코드는 int를 넘겨받으면 제대로 동작하지 않을 수 있습니다. 예를 들어, 3 & 4는 0이지만, 3과 4는 진리값으로 간주할 때 둘 다 참입니다. - ‘bool’이라는 이름을 바꿔야 합니까?
=> 아니오.
일부 검토자들은 bool 대신 boolean을 써야 한다고 주장했습니다. 그쪽이 더 이해하기 쉽다는 이유(초보자들은 불 대수(Boolean algebra)는 들어봤어도 bool과 연결 짓지 못할 수 있습니다)에서거나, 약어를 싫어해서였습니다. 제 생각은 이렇습니다: 파이썬은 ‘def’, ‘int’, ‘dict’처럼 약어를 신중하게 사용하며, 저는 이것이 이해에 부담이 된다고 생각하지 않습니다. 초보자에게는 그것이 와플이라 불리든 bool이라 불리든 상관없습니다. 그것은 새로운 단어일 뿐이며, 그들은 그 의미를 빠르게 배웁니다.
한 검토자는 ‘truth’라는 이름을 만들자고 주장했습니다. 저는 이것이 매력적이지 않은 이름이라고 생각하며, 오히려 이 용어는 (문서에서) 파이썬에 이미 존재하는 더 추상적인 개념인 진리값(truth value)을 가리키는 데 남겨두고 싶습니다. 예를 들면 다음과 같습니다: “컨테이너가 진리값으로 해석될 때, 빈 컨테이너는 거짓으로 간주되고 비어 있지 않은 컨테이너는 참으로 간주됩니다.”
- 앞으로는 (“if”, “and”, “not” 같은) 불리언 연산이 인자로 bool을 요구하도록 강제하는 방향으로 노력해야 합니까? 예를 들어 “if []:”가 더 이상 허용되지 않고 “if bool([]):”라고 써야 하도록 말입니다???
=> 아닙니다!!!
일부 사람들은 교과서적인 불리언 타입을 갖춘 언어는 이렇게 동작해야 한다고 믿습니다. 이 문제가 제기되었기 때문에, 제가 이 입장에 동의할지도 모른다고 걱정하는 사람들도 있었습니다. 이에 대한 제 입장을 아주 분명히 밝히겠습니다. 이것은 이 PEP의 동기에 포함되지 않으며, 저는 이런 변경을 할 생각이 없습니다. (아래의 “Clarification” 절도 참고하십시오.)
근거
대부분의 언어는 결국 불리언 타입을 갖추게 됩니다. 아직 널리 채택되지는 않았지만 새롭고 개선된 C 표준인 C99조차도 이를 갖추고 있습니다.
많은 프로그래머들이 불리언 타입의 필요성을 느끼는 것으로 보입니다. 대부분의 파이썬 문서에는 불리언 타입이 없다는 점에 대해 약간의 변명 같은 내용이 담겨 있습니다. 저는 파일 맨 위에 “False=0”과 “True=1”(또는 유사한) 상수를 정의하고 그것을 사용하는 모듈을 많이 봐왔습니다. 이 방식의 문제는 사람마다 다르게 한다는 점입니다. 예를 들어, “FALSE”, “false”, “False”, “F”, 심지어 “f” 중 무엇을 써야 합니까? 그리고 false는 값 0이어야 합니까, None이어야 합니까, 아니면 “true”나 “false”로 출력되는 다른 타입의 진리값이어야 합니까? 언어에 표준 bool 타입을 추가하면 이러한 문제들이 해결됩니다.
(데이터베이스나 RPC 패키지 같은) 일부 외부 라이브러리는 불리언 값과 정수 값을 구별할 수 있어야 하는데, 대개는 해결책을 마련할 수 있다 해도 언어가 표준 불리언 타입을 제공한다면 더 쉬울 것입니다. 이는 Jython에도 해당됩니다: 일부 Java 클래스는 int 인자와 boolean 인자에 대해 별도로 오버로드된 메서드나 생성자를 가지고 있습니다. bool 타입은 boolean 버전을 선택하는 데 사용될 수 있습니다. (일부 COM 인터페이스도 마찬가지인 것으로 보입니다.)
표준 bool 타입은 또한 어떤 값을 강제로 불리언으로 해석시키는 수단으로도 쓰일 수 있으며, 이는 불리언 값을 정규화하는 데 사용될 수 있습니다. 불리언 값을 두 값 중 하나로 정규화해야 할 때, bool(x)는 “not not x”보다 훨씬 명확하며, 다음보다 훨씬 간결합니다.
if x:
return 1
else:
return 0
다음은 파이썬을 가르치면서 얻은 몇 가지 논거입니다. 대화형 셸에서 사람들에게 비교 연산자 등을 보여줄 때, 이는 다소 보기 흉하다고 생각합니다:
>>> a = 13
>>> b = 12
>>> a > b
1
>>>
만약 이것이:
>>> a > b
True
>>>
0이나 1이 출력될 때마다 매번 1밀리초 덜 생각해도 될 것입니다.
다음과 같은 것을 보았을 때, (한동안 이 언어를 떠나 있던 경험 많은 파이썬 사용자들조차 당황하는 것을 본 적이 있는) 문제도 있습니다:
>>> cmp(a, b)
1
>>> cmp(a, a)
0
>>>
cmp()도 진리값을 반환한다고 믿고 싶어질 수 있지만, 실제로는 (-1, 0, 1)이라는 세 가지 값을 반환할 수 있습니다. 정수가 (통상적으로) 불리언 결과를 나타내는 데 사용되지 않았다면, 이는 완전히 다른 무언가로서 훨씬 더 명확하게 두드러졌을 것입니다.
명세
다음 파이썬 코드는 새 타입의 속성 대부분을 명시합니다:
class bool(int):
def __new__(cls, val=0):
# This constructor always returns an existing instance
if val:
return True
else:
return False
def __repr__(self):
if self:
return "True"
else:
return "False"
__str__ = __repr__
def __and__(self, other):
if isinstance(other, bool):
return bool(int(self) & int(other))
else:
return int.__and__(self, other)
__rand__ = __and__
def __or__(self, other):
if isinstance(other, bool):
return bool(int(self) | int(other))
else:
return int.__or__(self, other)
__ror__ = __or__
def __xor__(self, other):
if isinstance(other, bool):
return bool(int(self) ^ int(other))
else:
return int.__xor__(self, other)
__rxor__ = __xor__
# Bootstrap truth values through sheer willpower
False = int.__new__(bool, 0)
True = int.__new__(bool, 1)
False와 True 값은 None처럼 싱글턴이 됩니다. 이 타입은 값이 두 개이므로, 어쩌면 이를 “더블턴(doubleton)”이라고 불러야 하지 않을까요? 실제 구현에서는 bool의 다른 인스턴스가 생성되는 것을 허용하지 않습니다.
True와 False는 피클링과 마셜링을 통해 제대로 왕복됩니다. 예를 들어 pickle.loads(pickle.dumps(True))는 True를 반환하며, marshal.loads(marshal.dumps(True))도 마찬가지입니다.
불리언 결과를 반환하도록 정의된 모든 내장 연산은 0이나 1 대신 False나 True를 반환하도록 변경됩니다. 특히 이는 비교 연산(<, <=, ==, !=, >, >=, is, is not, in, not in), 단항 연산자 ‘not’, 내장 함수 callable(), hasattr(), isinstance()및 issubclass(), 딕셔너리 메서드 has_key(), 문자열 및 유니코드 메서드 endswith(), isalnum(), isalpha(), isdigit(), islower(), isspace(), istitle(), isupper(), startswith(), 유니코드 메서드 isdecimal()과 isnumeric(), 그리고 파일 객체의 ‘closed’ 속성에 영향을 미칩니다. operator 모듈의 프레디케이트들도 operator.truth()를 포함하여 bool을 반환하도록 변경됩니다.
bool은 int를 상속하므로, True+1은 유효하며 2와 같으며, 그 밖에도 마찬가지입니다. 이는 하위 호환성을 위해 중요합니다. 비교 연산 등이 현재 정수 값을 반환하고 있기 때문에, 기존 애플리케이션이 이 값들을 어떻게 사용하고 있는지 알 방법이 없습니다.
시간이 지나면서 표준 라이브러리는 적절한 경우 False와 True를 사용하도록 갱신될 것으로 예상됩니다(다만 이전에 int가 허용되던 곳에서 bool 인자 타입을 요구하지는 않습니다). 이 변경은 추가적인 문제를 일으키지 않을 것으로 예상되며, 이 PEP에서 상세히 명시하지는 않습니다.
C API
헤더 파일 “boolobject.h”는 bool 타입에 대한 C API를 정의합니다. 이는 “Python.h”에 포함되어 있으므로 직접 포함할 필요가 없습니다.
기존 이름 Py_False와 Py_True는 고유한 bool 객체 False와 True를 참조합니다(이전에는 값이 0과 1인 정적 int 객체를 참조했는데, 이는 int 값들 사이에서 고유하지 않았습니다).
새로운 API인 PyObject *PyBool_FromLong(long)은 C의 long int 인자를 받아, 인자가 0이면 Py_False에 대한, 0이 아니면 Py_True에 대한 새 참조를 반환합니다.
객체가 bool인지 확인하려면 매크로 PyBool_Check()를 사용할 수 있습니다.
bool 인스턴스의 타입은 PyBoolObject *입니다.
bool 타입 객체는 PyBool_Type으로 사용할 수 있습니다.
명확화
이 PEP는 거의 모든 객체 타입이 진리값으로 사용될 수 있다는 사실을 바꾸지 않습니다. 예를 들어 if 문에서 사용될 때, 빈 리스트는 거짓이고 비어 있지 않은 리스트는 참입니다. 이는 변하지 않으며, 앞으로도 이를 바꿀 계획은 없습니다.
변경되는 유일한 부분은 반환되거나 명시적으로 할당될 때 진리값을 나타내는 데 선호되는 값입니다. 이전에는 이러한 선호되는 진리값이 0과 1이었으나, 이 PEP는 선호되는 값을 False와 True로 변경하고, 내장 연산이 이 선호되는 값을 반환하도록 변경합니다.
호환성
하위 호환성 때문에 bool 타입에는 일부 사람들이 원하는 여러 속성이 빠져 있습니다. 예를 들어, bool 인자를 하나 또는 둘 사용하는 산술 연산이 허용되며, 이때 False는 0으로, True는 1로 취급됩니다. 또한 bool은 시퀀스 인덱스로 사용될 수 있습니다.
저는 이것을 문제로 보지 않으며, 언어를 이러한 방향으로 발전시키고 싶지도 않습니다. “불리언성(Booleanness)”에 대한 더 엄격한 해석이 언어를 더 명확하게 만든다고는 생각하지 않습니다.
호환성 요구사항의 또 다른 결과는 표현식 “True and 6”의 값이 6이 되고, 마찬가지로 표현식 “False or None”의 값이 None이 된다는 것입니다. “and”와 “or” 연산자는 결과를 결정하는 첫 번째 인자를 반환하도록 유용하게 정의되어 있으며, 이는 변경되지 않습니다. 특히 이 연산자들은 결과를 bool로 강제하지 않습니다. 물론 두 인자가 모두 bool이면 결과는 항상 bool입니다. 예를 들어 “bool(x and y)”와 같이 작성하면 쉽게 bool로 강제 변환할 수도 있습니다.
해결된 문제
(위의 검토 섹션도 참조하십시오.)
- bool 값의
repr()이나str()은 int 값과 다르기 때문에, 일부 코드(예를 들어 doctest 기반 단위 테스트, 그리고 “%s” % truth와 같은 것에 의존하는 데이터베이스 코드일 수 있음)가 실패할 수 있습니다. (bool 타입을 명시적으로 참조하지 않고도) 이를 쉽게 우회할 수 있으며, 이는 쉽게 고칠 수 있는 매우 적은 양의 코드에만 영향을 미칠 것으로 예상됩니다. - 다른 언어(C99, C++, Java)는 상수 이름을 모두 소문자로 “false”와 “true”라고 부릅니다. Python의 경우, 저는 기존 내장 상수들이 세운 예시, 즉 모두 CapitalizedWords 형식을 사용하는
None,Ellipsis,NotImplemented(그리고 모든 내장 예외 역시)의 방식을 따르는 것을 선호합니다. Python의 내장 네임스페이스는 함수와 타입에 대해서만 모두 소문자를 사용합니다. - 사용자의 기대를 충족시키기 위해, 불리언 맥락에서 참으로 간주되는 모든 x에 대해 표현식
x == True가 참이어야 하고, 마찬가지로 x가 거짓으로 간주되면x == False가 참이어야 한다는 제안이 있었습니다. 특히 불리언 변수에 대해 막 배운 초보자들은 다음과 같이 작성하기 쉽습니다if x == True: ...
다음과 같은 올바른 형식 대신,
if x: ...
많은 사람이 처음에는 후자의 형식을 불편해하는 데에는 강한 심리적, 언어적 이유가 있는 것으로 보이지만, 해법은 언어를 불구로 만드는 것이 아니라 교육에 있어야 한다고 믿습니다. 결국 ==는 일반적으로 추이적 연산자로 간주되며, 이는
a==b와b==c로부터a==c를 추론할 수 있음을 의미합니다. 하지만True와의 비교가 다른 피연산자가 어떤 타입이든 참인 값이기만 하면 동등하다고 보고한다면,6==True==7과 같은 어처구니없는 식이 참이 되어버려, 이로부터 거짓인6==7을 추론할 수 있게 됩니다. 이는 받아들일 수 없습니다. (게다가 이는 하위 호환성을 깨뜨리게 됩니다. 하지만 설령 그렇지 않더라도, 앞서 말한 이유들 때문에 저는 여전히 이에 반대할 것입니다.)또한 초보자에게는 다음과 같이 쓸 이유가 결코 없다는 점을 상기시켜야 합니다.
if bool(x): ...
“if” 안에는 bool이 암묵적으로 내포되어 있기 때문입니다. 여기서는 명시적인 것이 암묵적인 것보다 낫지 않습니다. 추가된 문구가 가독성을 해칠 뿐이고 다른 해석의 여지가 없기 때문입니다. 그러나 다음과 같이 쓸 이유가 있는 경우도 간혹 있습니다.
b = bool(x)
이는 임의의 객체 x에 대한 참조를 유지하는 것이 바람직하지 않거나, 다른 이유로 정규화가 필요한 경우에 유용합니다. 다음과 같이 쓰는 것이 적절한 경우도 간혹 있습니다.
i = int(bool(x))
이는 bool을 값 0 또는 1을 갖는 int로 변환합니다. 이는 이후로 그 값을 int로 사용하겠다는 의도를 전달합니다.
구현
C로 작성된 완전한 구현이 SourceForge 패치 관리자에 업로드되었습니다: https://bugs.python.org/issue528022
이는 곧 python 2.3a0을 위해 CVS에 체크인될 예정입니다.
Copyright
This document has been placed in the public domain.