Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 增强提案

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,它有两个常量,FalseTrue。 bool 类型将是 int 类型(在 C 中)的直接子类型,并且值 FalseTrue 在大多数情况下表现得像 0 和 1(例如,False==0True==1 将为真),除了 repr()str()。 所有概念上返回布尔结果的内置操作都将更改为返回 FalseTrue 而不是 0 或 1; 例如,比较、"not" 运算符,以及像 isinstance() 这样的谓词。

审查

我已经收集了足够多的反馈,足以让我终生受用,所以我正式宣布审查期结束。 我今天吃了中国菜; 我的幸运饼干上写着“强硬和刻薄的话语表明理由薄弱”。 这让我想起了一些反对这个 PEP 的帖子……:-)

无论如何,以下是我的 BDFL 宣告。(执行摘要:我不会更改任何内容;所有变体都被拒绝。)

  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 认为它应该返回 int,但几乎所有其他审阅者都同意它应该返回 bool。 我的理由:operator.truth() 存在是为了在其参数上强制布尔上下文(它调用 C API PyObject_IsTrue())。 结果是报告为 int 还是 bool 是次要的; 如果存在 bool,就没有理由不使用它。(在 PEP 中,operator.truth() 现在成为 bool() 的别名; 这很好。)

  6. bool 应该继承自 int 吗?

    => 是。

    在理想情况下,bool 可能会更好地实现为一个单独的整数类型,该类型知道如何执行混合模式算术。 但是,让 bool 继承自 int 极大地简化了实现(部分原因是所有调用 PyInt_Check() 的 C 代码将继续工作 - 这对于 int 的子类来说返回 true)。 此外,我相信这在可替换性方面是正确的:需要 int 的代码可以被 fed bool,它将表现得与 0 或 1 一样。 需要 bool 的代码在被给定 int 时可能无法正常工作; 例如,3 & 4 为 0,但 3 和 4 都在被视为真值时为真。

  7. 应该更改名称 ‘bool’ 吗?

    => 不。

    一些审阅者主张使用 boolean 而不是 bool,因为这更容易理解(新手可能听说过布尔代数,但可能没有将它与 bool 联系起来)或者因为他们讨厌缩写。 我的看法:Python 谨慎地使用缩写(如 ‘def’、‘int’、‘dict’),我不认为这些是理解的负担。 对于新手来说,它被称为华夫饼还是布尔值并不重要; 它是一个新词,他们很快就会学会它的含义。

    一位审阅者主张将名称改为 ‘truth’。 我发现这是一个不吸引人的名称,实际上更愿意将此术语(在文档中)保留给 Python 中已经存在的更抽象的真值概念。 例如:“当容器被解释为真值时,空容器被视为假,非空容器被视为真。”

  8. 我们应该努力要求布尔运算(如“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)

FalseTrue 将是单例,就像 None 一样。 由于该类型有两个值,也许应该将它们称为“双例”? 实际实现将不允许创建 bool 的其他实例。

TrueFalse 将正确地通过 pickle 和序列化进行来回转换; 例如,pickle.loads(pickle.dumps(True)) 将返回 Truemarshal.loads(marshal.dumps(True)) 也是如此。

所有定义为返回布尔结果的内置操作都将更改为返回 FalseTrue 而不是 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,依此类推。 这对于向后兼容性很重要:因为比较等目前返回整数值,所以无法判断现有应用程序如何使用这些值。

预计随着时间的推移,标准库将更新为在适当情况下使用 FalseTrue(但不会在以前允许使用 int 的地方要求 bool 参数类型)。此更改不应造成额外问题,并且在本 PEP 中没有详细说明。

C API

头文件“boolobject.h”定义了 bool 类型的 C API。它包含在“Python.h”中,因此无需直接包含它。

现有的名称 Py_FalsePy_True 引用唯一的 bool 对象 FalseTrue(以前这些引用了值为 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 将优先值更改为 FalseTrue,并更改内置操作以返回这些优先值。

兼容性

由于向后兼容性,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,我更喜欢坚持现有内置常量设置的示例,这些示例都使用大写单词:NoneEllipsisNotImplemented(以及所有内置异常)。Python 的内置命名空间仅对函数和类型使用全小写。
  • 有人建议,为了满足用户的期望,对于在布尔上下文中被视为真的每个 x,表达式 x == True 应该为真,同样如果 x 被视为假,x == False 应该为真。特别是刚学习布尔变量的新手可能会写
    if x == True: ...
    

    而不是正确的形式,

    if x: ...
    

    似乎有强烈的心里和语言原因,导致许多人最初对后一种形式感到不舒服,但我认为解决方案应该在教育而不是削弱语言。毕竟,== 通常被视为一个传递运算符,这意味着从 a==bb==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

上次修改:2023-09-09 17:39:29 GMT