PEP 285 – 添加 bool 类型
- 作者:
- Guido van Rossum <guido at python.org>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2002-03-08
- Python 版本:
- 2.3
- 历史记录:
- 2002-03-08, 2002-03-30, 2002-04-03
摘要
本 PEP 提案引入一个新的内置类型 bool,它有两个常量,False
和 True
。 bool 类型将是 int 类型(在 C 中)的直接子类型,并且值 False
和 True
在大多数情况下表现得像 0 和 1(例如,False==0
和 True==1
将为真),除了 repr()
和 str()
。 所有概念上返回布尔结果的内置操作都将更改为返回 False
或 True
而不是 0 或 1; 例如,比较、"not" 运算符,以及像 isinstance()
这样的谓词。
审查
我已经收集了足够多的反馈,足以让我终生受用,所以我正式宣布审查期结束。 我今天吃了中国菜; 我的幸运饼干上写着“强硬和刻薄的话语表明理由薄弱”。 这让我想起了一些反对这个 PEP 的帖子……:-)
无论如何,以下是我的 BDFL 宣告。(执行摘要:我不会更改任何内容;所有变体都被拒绝。)
- 这个 PEP 应该被接受吗?
=> 是。
有许多反对这个 PEP 的论点。 其中许多是基于误解。 我已经尝试在 PEP 的正文中澄清一些最常见的误解。 对我来说,唯一让我有点犹豫的是新手写 “if x == True” 而不是 “if x” 的倾向。 下面也将详细介绍这一点。 我认为这不是拒绝这个 PEP 的充分理由。
- 应该
str(True)
返回 “True” 还是 “1”? “1” 可能会减少向后兼容性问题,但看起来很奇怪。(repr(True)
将始终返回 “True”。)=> “True”。
几乎所有审阅者都同意这一点。
- 常量应该叫做 ‘True’ 和 ‘False’(类似于 None)还是 ‘true’ 和 ‘false’(如 C++、Java 和 C99 中)?
=> True 和 False。
大多数审阅者同意,在 Python 中保持一致性比与其他语言保持一致性更重要。
- 我们应该努力在将来通过合适的警告来消除对布尔值的非布尔运算,以便例如 True+1 最终(在 Python 3000 中)将是非法的吗?
=> 不。
一小部分声音很大的少数人希望看到“教科书式”的布尔值,这些布尔值根本不支持算术运算,但大多数审阅者同意我,布尔值应该始终允许算术运算。
- 应该
operator.truth(x)
返回 int 还是 bool?=> bool。
Tim Peters 认为它应该返回 int,但几乎所有其他审阅者都同意它应该返回 bool。 我的理由:
operator.truth()
存在是为了在其参数上强制布尔上下文(它调用 C APIPyObject_IsTrue())
。 结果是报告为 int 还是 bool 是次要的; 如果存在 bool,就没有理由不使用它。(在 PEP 中,operator.truth()
现在成为bool()
的别名; 这很好。) - bool 应该继承自 int 吗?
=> 是。
在理想情况下,bool 可能会更好地实现为一个单独的整数类型,该类型知道如何执行混合模式算术。 但是,让 bool 继承自 int 极大地简化了实现(部分原因是所有调用
PyInt_Check()
的 C 代码将继续工作 - 这对于 int 的子类来说返回 true)。 此外,我相信这在可替换性方面是正确的:需要 int 的代码可以被 fed bool,它将表现得与 0 或 1 一样。 需要 bool 的代码在被给定 int 时可能无法正常工作; 例如,3 & 4 为 0,但 3 和 4 都在被视为真值时为真。 - 应该更改名称 ‘bool’ 吗?
=> 不。
一些审阅者主张使用 boolean 而不是 bool,因为这更容易理解(新手可能听说过布尔代数,但可能没有将它与 bool 联系起来)或者因为他们讨厌缩写。 我的看法:Python 谨慎地使用缩写(如 ‘def’、‘int’、‘dict’),我不认为这些是理解的负担。 对于新手来说,它被称为华夫饼还是布尔值并不重要; 它是一个新词,他们很快就会学会它的含义。
一位审阅者主张将名称改为 ‘truth’。 我发现这是一个不吸引人的名称,实际上更愿意将此术语(在文档中)保留给 Python 中已经存在的更抽象的真值概念。 例如:“当容器被解释为真值时,空容器被视为假,非空容器被视为真。”
- 我们应该努力要求布尔运算(如“if”、“and”、“not”)将来拥有 bool 作为参数,以便例如“if []:” 将变为非法,并且必须写成“if bool([]):” ???
=> 不!!!
有些人认为这是一种拥有教科书式布尔类型的语言应该表现的方式。 由于它被提了出来,其他人担心我可能会同意这种观点。 让我明确说明我在这方面的立场。 这不是 PEP 的动机,我无意做出这种改变。(另请参阅下面的“澄清”部分。)
基本原理
大多数语言最终都会发展出布尔类型; 即使 C99(新的和改进的 C 标准,尚未被广泛采用)也拥有一个。
许多程序员显然感到需要布尔类型; 大多数 Python 文档都包含了一段对缺少布尔类型的道歉。 我已经看到很多模块在顶部定义了常量“False=0”和“True=1”(或类似的)并使用了它们。 这样做的问题是,每个人都用不同的方式来做。 例如,你应该使用“FALSE”、“false”、“False”、“F” 还是甚至“f”? 并且 false 应该为零值还是 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
以下是从教 Python 中得出的一些论据。 当在交互式 shell 中向人们展示比较运算符等时,我认为这有点难看
>>> a = 13
>>> b = 12
>>> a > b
1
>>>
如果这是
>>> a > b
True
>>>
每次打印 0 或 1 时,它将需要少思考一毫秒。
还有一个问题(我看到即使是离开 Python 有一段时间经验丰富的 Pythonista 也被困扰),如果你看到
>>> cmp(a, b)
1
>>> cmp(a, a)
0
>>>
你可能会倾向于相信 cmp()
也返回真值,而实际上它可以返回三个不同的值 (-1, 0, 1)
。 如果 int 不(通常)用于表示布尔结果,那么这将更清楚地突出显示为完全不同的东西。
规范
以下 Python 代码指定了新类型的多数属性
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 一样。 由于该类型有两个值,也许应该将它们称为“双例”? 实际实现将不允许创建 bool 的其他实例。
True
和 False
将正确地通过 pickle 和序列化进行来回转换; 例如,pickle.loads(pickle.dumps(True))
将返回 True
,marshal.loads(marshal.dumps(True))
也是如此。
所有定义为返回布尔结果的内置操作都将更改为返回 False
或 True
而不是 0 或 1。 特别是,这影响了比较(<
、<=
、==
、!=
、>
、>=
、is、is not、in、not in)、一元运算符 'not'、内置函数 callable()
、hasattr()
、isinstance()
和 issubclass()
、dict 方法 has_key()
、字符串和 unicode 方法 endswith()
、isalnum()
、isalpha()
、isdigit()
、islower()
、isspace()
、istitle()
、isupper()
和 startswith()
、unicode 方法 isdecimal()
和 isnumeric()
,以及文件对象的 'closed' 属性。 operator 模块中的谓词也更改为返回 bool,包括 operator.truth()
。
由于 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 参数并返回对 Py_False
(当参数为零时)或 Py_True
(当它不为零时)的新的引用。
要检查对象是否为 bool,可以使用宏 PyBool_Check()
。
bool 实例的类型为 PyBoolObject *
。
bool 类型对象可作为 PyBool_Type 获得。
澄清
本 PEP 不会改变几乎所有对象类型都可以用作真值这一事实。例如,在 if 语句中使用时,空列表为假,非空列表为真;这不会改变,也没有计划改变这一点。
唯一改变的是在显式返回或赋值时表示真值的优先值。以前,这些优先真值为 0 和 1;PEP 将优先值更改为 False
和 True
,并更改内置操作以返回这些优先值。
兼容性
由于向后兼容性,bool 类型缺乏一些人希望看到的许多属性。例如,允许对一个或两个 bool 参数进行算术运算,将 False
视为 0,将 True
视为 1。此外,bool 可用作序列索引。
我认为这不是问题,也不想让语言朝着这个方向发展。我不认为对“布尔值”的更严格解释会让语言更清晰。
兼容性要求的另一个结果是表达式“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,我更喜欢坚持现有内置常量设置的示例,这些示例都使用大写单词:
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 中。
版权
本文档已放置在公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0285.rst