为什么我不能在Python中扩展bool类型?

41
>>> class BOOL(bool):
...     print "why?"
... 
why?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    type 'bool' is not an acceptable base type

我认为Python信任程序员。


12
你想添加一个FileNotFound值吗? - Juliano
1
在这里进行“组合 vs. 继承”的谷歌搜索或SO搜索可能会有所帮助。 - Tyler
6个回答

61

Guido 的看法:

昨晚我考虑了一下,意识到你根本不应该被允许子类化 bool!一个子类只有在拥有实例时才有用,但是一个 bool 子类的存在将会打破 True 和 False 是 bool 的唯一实例这个不变量!(C 的子类的实例也是 C 的实例。)我认为不提供创建额外 bool 实例的后门很重要,因此 bool 不应该是可子类化的。

参考资料:http://mail.python.org/pipermail/python-dev/2002-March/020822.html


21
真和假是单例的。这是一个更好的答案。 - Juanjo Conti
1
很高兴知道事情是一致的:class Foo(None.__class__): ... -> TypeError: type 'NoneType' is not an acceptable base type - Wayne Werner
2
尝试理解这个答案:从bool继承不会以任何方式影响TrueFalse的不变性。子类的实例仍然是TrueFalse(从bool的角度来看)。如果您覆盖了__eq __(),则它可能具有其他属性并且可能与其中一个相等,但否则它不会对bool或其两个可能值产生负面影响。我错过了什么? - user3204459
1
如果您继承bool并将此新类类型的实例传递给方法,则新实例将破坏现有的布尔逻辑(您无法满足继承bool的Liskov替换原则)。以前从未有过一个不遵守身份检查的bool实例。您会破坏基本语言谓词,如if b and isinstance(b, bool) then b is True。这将导致您的代码跟随库设计人员不打算的路径,并使语言感觉不稳定。 - flakes
@flakes 但这对于任何类的继承都是正确的(例如考虑 if not b and isinstance(b, int) then b is 0,其中一个类继承自 int)。那么 bool 有什么特别之处呢?从 bool 继承不应该破坏任何东西,一切仍将按照其预期进行。== 将调用 __eq__(),而 is 将检查标识,因此使用正确运算符的人将获得正确的行为。使用标识而不是相等或反之似乎是禁止编程语言中某些任意事物的奇怪原因,因为任何东西都可能被误用。 - user3204459
显示剩余4条评论

12
如果你使用的是Python 3并且想要创建一个可以被视为布尔值的类,同时还包含其他功能,那么就需要在类中实现__bool__方法。
在Python 2中,可以通过实现__nonzero____len__(如果你的类是一个容器)来实现相同的效果。

3
在Python 2.x中,您可以通过实现__nonzero____len__来完成相同的操作。 - Max Shawabkeh
我想让1和2返回我的类的一个实例。你会怎么做? - Juanjo Conti
2
为什么不从头开始创建自己的类,而是先子类化bool,然后覆盖int方法呢? - Agos

12

由于OP在评论中提到:

我希望1和2返回我的类的一个实例。

我认为重要的是指出这是完全不可能的:Python不允许您更改内置类型(特别是它们的特殊方法)。 字面值1将始终是内置类型int的实例,而且无论如何,and运算符的基本语义都不能被覆盖 - 对于任何aba and b始终等同于b if a else a(没有bool强制转换,尽管OP似乎错误地认为正在发生一种转换)。

重申这个关键点: a and b的值始终是ab,不能改变这种语义约束(即使ab是您自己奇特类的实例 - 当然当它们被限制为Python的内置int的实例时就更加不可能了!-)。


无法更改内置类型?这不是 forbiddenfruit 库 所做的吗? - gerrit

9

3
因为 bool 只有两个值——TrueFalse。如果您能够对 bool 进行子类化,就可以为其定义任意数量的值,这绝对不是您想要发生的事情。
更好的问题是:您为什么想扩展 bool?

我想添加一个属性,让我在对象上跟踪一些东西。 - Juanjo Conti
4
@Juanjo,那么你想要的不是一个布尔值,而是一个包含布尔值和其他内容的元组。布尔值必须是True或False,不能有其他值或属性,否则该类的实例将无法与True和False做比较。如果发生这种情况,它就不再是布尔值了。 - Juliano
好的,但我无法覆盖int.__nonzero__以返回这个元组。我可以吗? - Juanjo Conti
答案:模糊逻辑,重载布尔运算符等。 - tox123
重载魔术方法并添加额外的方法,同时保持布尔运算一致。例如,使用几个混合类来创建一个布尔类,该类可以轻松地转换为/从ctypes和字符串,并且符合C自定义网络协议API的要求。 - Tristan Duquesne

0
我认为没有特别好的理由禁止对bool进行子类化。
Guido说(正如在得票最高、被接受的答案中引用的那样)这样做可以保持TrueFalsebool的唯一实例的不变性。但事实并非如此:
>>> class BOOL:
...     @property
...     def __class__(self):
...         return bool
...
>>> FileNotFound = BOOL()
>>> isinstance(FileNotFound, bool)
True
>>>
__class__的这种行为是文档化并在标准库中使用。
我猜你可以争论说禁止子类化仍然保留了一种"道德"上的封闭性,但对我来说,这有什么好处并不清楚。阅读Guido的解释,一个合理的Python程序员会得出结论,他是在说isinstance(x, bool)x is True or x is False的安全等价物,但事实并非如此。
此外,如果确切的类型对你很重要,你应该测试type(x) is T,而不是isinstance(x, T),无论如何。即使它们在bool的情况下等效(即使它们成功做到了),也只会鼓励编写在那种情况下无法清晰表达意图的代码。
type(FileNotFound)BOOL,而不是 bool,所以确切类型的测试在这里有效。我猜想,一个恶意行为者可能会替换 type。一般来说,在Python中期望任何形式的不变量都是愚蠢的行为。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接