乐趣区

关于python:Python-为什么要在-18-年前引入布尔类型且与-CC-和-Java-都不同

花下猫语:在上一篇《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,它将蕴含两个常量FalseTrue。这个 bool 类型是 int 类型的间接子类型(在 C 中),并且在除了 repr() 和 str() 之外的大多数方面,它的值 FalseTrue 都将体现得像是 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…

退出移动版