Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

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,包含两个常量 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 的代码可以被传入 bool,并且它的行为与 0 或 1 相同。需要 bool 的代码在给定 int 时可能无法工作;例如,3 & 4 是 0,但 3 和 4 在被视为真值时都为真。

  7. “bool”这个名字应该改吗?

    => 否。

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

    一位审阅者主张将名称改为“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 类型可以用于选择布尔变体。(对于某些 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)

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

TrueFalse 将通过 pickle 和 marshalling 正确地往返;例如 pickle.loads(pickle.dumps(True)) 将返回 Truemarshal.loads(marshal.dumps(True)) 也将返回 True

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

预计随着时间的推移,标准库将进行更新,在适当的时候使用 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(当参数非零时)的新引用。

要检查对象是否为布尔值,可以使用宏 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。

已解决的问题

(另请参阅上面的审查部分。)

  • 因为布尔值的 repr()str() 与整数值不同,所以一些代码(例如基于 doctest 的单元测试,以及可能依赖于“%s”% truth 之类的数据库代码)可能会失败。很容易解决这个问题(无需显式引用布尔类型),预计这只会影响极少量可以轻松修复的代码。
  • 其他语言(C99、C++、Java)将常量命名为“false”和“true”,全部小写。对于 Python,我更喜欢沿用现有内置常量所设定的例子,它们都使用 CapitalizedWords: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”中是隐式的。在这里,显式**不**比隐式好,因为多余的冗词会损害可读性,并且没有其他可能的解释。然而,有时有理由写

    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

最后修改:2025-02-01 08:55:40 GMT