PEP 285 – 添加 bool 类型
- 作者:
- Guido van Rossum <guido at python.org>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2002年3月8日
- Python 版本:
- 2.3
- 发布历史:
- 2002年3月8日,2002年3月30日,2002年4月3日
摘要
本 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 的代码可以被传入 bool,并且它的行为与 0 或 1 相同。需要 bool 的代码在给定 int 时可能无法工作;例如,3 & 4 是 0,但 3 和 4 在被视为真值时都为真。 - “bool”这个名字应该改吗?
=> 否。
一些审阅者主张使用 boolean 而不是 bool,因为这更容易理解(新手可能听说过布尔代数,但可能不会将其与 bool 联系起来),或者因为他们讨厌缩写。我的看法是:Python 审慎地使用缩写(如 'def'、'int'、'dict'),而且我认为这些不会成为理解的负担。对于新手来说,它被称为 waffle 还是 bool 都无关紧要;它是一个新词,他们很快就能学会它的含义。
一位审阅者主张将名称改为“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 类型可以用于选择布尔变体。(对于某些 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 时,思考时间就会减少一毫秒。
还有一个问题(我曾见过即使是经验丰富的 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 和 marshalling 正确地往返;例如 pickle.loads(pickle.dumps(True)) 将返回 True,marshal.loads(marshal.dumps(True)) 也将返回 True。
所有定义为返回布尔结果的内置操作都将更改为返回 False 或 True,而不是 0 或 1。特别是,这会影响比较(<, <=, ==, !=, >, >=, is, is not, in, not in)、一元运算符 'not'、内置函数 callable(), hasattr(), isinstance() 和 issubclass()、字典方法 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(当参数非零时)的新引用。
要检查对象是否为布尔值,可以使用宏 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。
已解决的问题
(另请参阅上面的审查部分。)
- 因为布尔值的
repr()或str()与整数值不同,所以一些代码(例如基于 doctest 的单元测试,以及可能依赖于“%s”% truth 之类的数据库代码)可能会失败。很容易解决这个问题(无需显式引用布尔类型),预计这只会影响极少量可以轻松修复的代码。 - 其他语言(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”中是隐式的。在这里,显式**不**比隐式好,因为多余的冗词会损害可读性,并且没有其他可能的解释。然而,有时有理由写
b = bool(x)
当保留对任意对象 x 的引用不具吸引力,或者由于某些其他原因需要标准化时,这很有用。有时也适合写
i = int(bool(x))
这将布尔值转换为值为 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