花下猫语:在上一篇《Python 为什么能反对任意的真值判断?》文章中,咱们剖析了 Python 在真值判断时的底层实现,能够看出 Python 在看待布尔值时,采纳了比拟宽泛的态度。官网对此是怎么思考的呢?
上面的文章是我刚翻译的 PEP-285,作者是 Python 之父 Guido van Rossum。这个 PEP 意义十分重大,Python 的 bool 类型就是从它开始引入的,而我在上篇文章中剖析到的很多问题,都能在这篇十几年前的文档中找到解释!另外它还回应了比拟典型的一些争议,值得大家理解下。
PEP 原文: https://www.python.org/dev/peps/pep-0285/
PEP 题目: PEP 285 — Adding a bool type
PEP 作者: Guido van Rossum
创立日期: 2002-03-08
合入版本: 2.3
译者:豌豆花下猫 @Python 猫公众号
PEP 翻译打算:https://github.com/chinesehua…
概要
本 PEP 提议引入一个新的内置类型 bool
,它将蕴含两个常量False
和True
。这个 bool 类型是 int 类型的间接子类型(在 C 中),并且在除了 repr() 和 str() 之外的大多数方面,它的值 False
和True
都将体现得像是 0 和 1(例如,False == 0 和 True == 1 都为真)。
所有在概念上需返回布尔后果的内置操作,都将更改为返回 False 或 True,而不再是 0 或 1,例如,比拟操作、“not”运算和 isinstance() 之类的断言办法。
评审
我曾经收集了太多太多的反馈意见,因而我发表:评审阶段(review period)正式 完结。 我明天吃的是中国菜,我的签语饼上写着:“Strong and bitter words indicate a weak cause.”它使我想起了一些拥护本 PEP 的帖子 … :-)
(译注:1、签语饼即 fortune cookies,这是一种美国文化特色。美国的中餐馆在结账的时候风行给客人一些写了签语的饼干,个别都是祝福语。2、那句签语出自维克多·雨果,意为:理亏者言辞强烈)
无论如何,这些是我的 BDFL 申明。(执行摘要(Executive summary):我不会更改任何内容;所有其它提议都会被回绝。)
1、本 PEP 应该被承受吗?
=> 是的。
有很多拥护本 PEP 的观点。其中少数是出于误会。我已尝试在上面的 PEP 注释中廓清一些最常见的误会。对我而言惟一值得思考的问题是老手们偏向于写“if x == True”,但“if x”就足够了。上面也有更多对于它的信息。我认为这不足以回绝本 PEP。
2、str(True)
应该返回“True”还是“1”?“1”可能会缩小向后兼容性问题,但看起来很奇怪。(repr(True)
将始终返回“True”。)
=>“True”。
简直所有评审人都批准这一点。
3、常量应该被命名为“True”和“False”(相似于 None)还是“true”和“false”(像 C++、Java 和 C99 那样)?
=>True 和 False。
大多数评审人都认为 Python 内的一致性要比跟其它语言的一致性更为重要。
4、是否应该通过适当的告警来打消对布尔值的非布尔运算,以便例如 True + 1 最终(在 Python 3000 中)变为非法的?
=> 不该
有一小部分观点嘹亮的人,心愿看到“教科书式”的布尔类型,即齐全不反对算术运算,但大多数评审人都批准我,认为布尔类型应该反对算术运算。
5、operator.truth(x)
应该返回 int 还是 bool?
=>bool。
Tim Peters 认为应该返回一个整数,然而简直所有其余评审人都认为应该返回一个布尔值。我的理由:operator.truth() 意味着强制其参数应用布尔类型上下文(它调用 C API PyObject_IsTrue()
)。无论后果是 int 还是 bool,都是主要的;如果有 bool,则没有理由不应用它。(在本 PEP 下,operator.truth() 成为了 bool() 的别名;这也能够。)
6、bool 应该继承自 int 吗?
=> 是的。
在现实的状况下,bool 最好是实现为一种独自的整数类型,且反对执行混合的算术操作。然而,从 int 继承出 bool 将极大地简化实现(局部起因是,所有调用PyInt_Check()
的 C 代码都可兼容——它对于 int 的子类会返回 true)。
另外,我认为这合乎可替换性(substitutability)概念:代码中须要 int 时,能够喂入 bool,它等同于 0 或 1。代码中须要 bool 时,若赋予 int,则可能不合乎预期;例如,3&4 计算为 0,然而当 3 和 4 被视为真值时,却都为真。
7、是否应该扭转“bool”的叫法?
=> 不。
一些评审人主张应用 boolean 而不是 bool,因为这样更容易了解(老手可能据说过布尔代数(Boolean algebra),但可能对 bool 无感),或者因为他们厌恶缩写。
我的观点:Python 明智地使用缩写(例如 ’def’、’int’、’dict’),我不认为这会造成了解的累赘。对于老手来说,无论它被叫作 waffle 还是 bool 都没关系;这只是一个新词,他们很快就能把握它的含意。
(译注:waffle,咱们个别熟知的意思是“华夫饼干”,但它还有个意思是“无意义的、无关紧要的、胡乱的话”)
一位评审人认为能够叫“truth”。我感觉这个叫法没有吸引力,实际上更偏向于保留该术语(在文档中),以指代在 Python 中曾经存在的具体的真值概念。例如:“当将一个容器解释为一个 truth 值时,空容器会被视为假,而非空容器则被视为真”。
8、未来是否应该要求布尔运算符(例如“if”、“and”和“not”)应用一个布尔值作为参数,例如令“if []:”变为非法的,要求必须写成“if bool([]):”???
=> 不!!!
有些人认为,这就是一门有教科书式布尔类型的语言应该的做法。因为它被提起了,所以其他人放心我可能会批准这一做法。
我来明确论述对此的立场:这不是本 PEP 的动机,我也无心进行更改。(另请参见上面的“廓清”局部。)
基本原理
大多数语言最终都会倒退出一个布尔类型,甚至 C99(新的改进版 C 规范,尚未宽泛采纳)也有一个。(译注:C99 规范诞生于 1999 年,本 PEP 写于 2002 年,时过境迁,现在 C99 规范基本上已是掉队的了)
许多程序员都感觉须要一种布尔类型,大多数 Python 文档因短少布尔类型而含有歉意。我看过很多模块,它们在顶部定义了常量“False = 0”和“True = 1”(或相似的常量),并应用它们。
问题是每个人的做法都不一样。例如,你应该应用“FALSE”、“false”、“False”、“F”还是“f”呢?另外,假值应该为 0 或 None,或是一个其它的布尔类型打印出“true”或“false”呢?在语言中增加一个规范的布尔类型能够解决这些问题。
一些内部库(例如数据库和 RPC 相干的包)须要可能辨别布尔值和整数值,只管通常能够制订出解决方案,但如果语言自身提供了规范的布尔类型,则会更容易。这也实用于 Jython:某些 Java 类具备别离用于 int 和 boolean 参数的重载办法或构造函数。布尔类型可用于抉择布尔变量。(显然,某些 COM 接口也是如此。)
规范的布尔类型(bool type)也能够作为强制将值解释为布尔值(Boolean)的办法,该办法可用于标准化布尔值。当一个布尔值须要归一化为两个值之一时,bool(x) 比“not not x”更清晰,也比这种写法更简洁:
if x:
return 1
else:
return 0
这是从传授 Python 中得出的一些教训。当向人们在交互式终端中展现比拟运算符时,我认为这有点难看:
>>> a = 13
>>> b = 12
>>> a > b
1
>>>
如果是这样的话:
>>> a > b
True
>>>
每次会少花一毫秒的工夫思考打印出的 0 或 1。
还有一个问题(它甚至困扰了已经经验丰富但远离了 Python 一段时间的人):
>>> cmp(a, b)
1
>>> cmp(a, a)
0
>>>
你可能会偏向于认为 cmp() 也返回一个布尔值,但实际上它能够返回三个不同的值(-1、0、1)。如果整数没有(通常)被用于示意布尔值后果,则这能够更加显著地表白出其它的含意。(译注:即只用 True/False 示意布尔值,则整数表白其它含意时就不会有歧义)
标准
以下 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 将是单例的(singletons),像 None 一样。因为这种类型有两个值,兴许应该将它们称为“doubletons”?理论的实现将不容许创立 bool 的其它实例。
True 与 False 会被正确地序列化和打包,例如 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(),字典办法 has_key(),字符串和 unicode 办法 endswith()、isalnum()、isalpha()、isdigit()、islower()、isspace()、istitle()、isupper() 和 startswith(),unicode 办法 isdecimal() 和 isnumeric(),以及文件对象的“closed”属性。operator 模块中的断言办法也被改为返回布尔值,包含 operator.truth()。
因为 bool 继承自 int,因而 True + 1 无效且等于 2,依此类推。这对于向后兼容性很重要:因为比拟之类的操作以后返回整数值,所以无奈确定现有应用程序怎么应用这些值。
预计随着工夫的推移,规范库将在适当的时候更新为应用 False 和 True(但在以前容许应用 int 的场合,则不须要应用 bool 参数类型)。此更改不应引起在本 PEP 中未具体阐明的其它问题。
C API
“boolobject.h”头文件为布尔类型定义了 C API。它蕴含在“Python.h”中,因而不须要再 include 它。
现有的名称 Py_False 和 Py_True 援用举世无双的布尔对象 False 和 True(之前,它们别离援用了值为 0 和 1 的动态整数对象,是泛滥整数之一)。
一个新的 API,即PyObject *PyBool_FromLong(long)
,会接管一个 C 长整型参数,并返回对 Py_False(当参数为零时)或 Py_True(当非零时)的新援用。
要查看对象是否为布尔对象,能够应用宏 PyBool_Check()。
布尔实例的类型是 PyBoolObject *。
布尔类型对象可作为 PyBool_Type 应用。
廓清
本 PEP 没有扭转一个事实,即简直所有类型的对象都能够用作虚实值。例如,在 if 语句中应用时,一个空列表为 false,一个非空列表为 true;这不会扭转,而且也不打算扭转。
惟一扭转的是在返回或赋值时,用于示意虚实值的首选值。以前,这些首选的虚实值是 1 和 0;本 PEP 将首选值更改为 True 和 False,并批改内置操作以返回这些首选值。
兼容性
因为要向后兼容,所以布尔类型领有一些不严格的属性。例如,容许应用布尔参数进行算术运算,行将 False 视为 0,将 True 视为 1。而且,能够将 bool 用作序列对象的索引。
我不认为这是一个问题,也不心愿朝这个方向倒退语言。我认为,对“布尔性(Booleanness)”的更严格的解释不会使语言更清晰。
兼容性要求的另一个后果是表达式“True and 6”的值为 6,相似地,表达式“False or None”的值为 None。
“and”和“or”运算符被设计来返回第一个决定了后果的参数,这点不会扭转;特地地,它们不强制要求后果为布尔类型。当然,如果两个参数都是布尔值,那么后果必定是一个布尔值。通过写“bool(x and y)”,也能够很容易地将其强制转成布尔类型。
解决了的问题
(另请参见下面的“评审”局部。)
- 因为 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”中。在这里,显式并 不 比隐式好,因为增加的词法会侵害可重用性,并且限度了解释器的解释行为。(译注:”The Zen of Python“中认为”显式比隐式好“,但在这里,Guido 认为隐式更好,所以他在原文档中加粗了”not“)
然而,有时候有理由写成:
b = bool(x)
当不须要保留对任意 x 对象的援用时,或者因为某些其它起因须要规范化时,这很有用。有时候这样写也很适合:
i = int(bool(x))
它将布尔值转换为整数的 0 或 1。传播了将该值用作 int 的用意。
实现
残缺的 C 实现代码已上传到 SourceForge 补丁管理器:https://bugs.python.org/issue…
它将很快被合入到 python 2.3a0 的 CVS 中。
版权
本文档已进入公共畛域。
源文档:https://github.com/python/pep…
更多的 PEP 中文翻译内容,可在 Github 查阅:https://github.com/chinesehua…