为什么isinstance(True, int)==True

前言

本篇文章来解释下为什么 Python 中 bool 属于 int 的子类。

起因

起因是我在写一个数据类型判断代码时,发现代码走到了错误的分支中,本来判断数据类型为 bool 型,应该走到 if 分支,结果错误的走到了 else 分支,遂搜索了一下,发现 bool 竟然是 int 的子类,来看代码:

1
2
3
4
5
6
7
a = True
if isinstance(a, int):
print(f'{a} is int!')
elif isinstance(a, bool):
print(f'{a} is bool!')
else:
print(f'{a} is not int or bool!')
1
True is int!

大家都知道,True 是一个布尔型的值,但是在判断时,发现输出了和我们预期不符的结果

问题原因

查了下,stackoverflow 上有一个关于此问题的讨论,此问题的出现是由于历史原因。

早期的 Python 是没有 bool 类型的,后来在 PEP 285 中添加了 bool 类型,在实现时,为了简化开发和向后兼容将 bool 类型从 int 类型继承,因为本来 int 就实现了 bool 的一部分功能(使用 1 代替 True,使用 0 代替 False),所以在使用 isinstance 函数进行类型判断时,会出现和我们预期不相符的结果。

bool 类型是 int 类型的子类是最根本的原因,在 Python 官方文档 中也提到了这个问题,可以使用 __mro__ 属性或 mro() 方法来确认。

__mro__ 属性或 mro() 方法用来打印类的继承顺序,因为在类中搜索方法(属性)时,方法(属性)可能存在于基类,也可能存在于子类,对于 Python 这种支持多重继承的语言来说,如何确定搜索顺序就比较困难。mro() 方法的出现使得类之间的继承顺序变成了线性的,所以可以很方便的确定类之间的继承顺序,从而使搜索类的方法(属性)得以简化。

Python 有多种方案来确定 MRO,Python2 中经典类使用深度遍历算法,Python3 使用 C3 算法,更多资料小伙伴可以自行查询。

1
2
3
print(bool.__mro__)
print(int.__mro__)
print(str.__mro__)
1
2
3
(<class 'bool'>, <class 'int'>, <class 'object'>)
(<class 'int'>, <class 'object'>)
(<class 'str'>, <class 'object'>)

可以看到, 三个类型都是 object 的子类,但是 bool 是 int 的子类,所以在判断 bool 的类型时,会出现错误的判断。

解决方案

既然了解了问题的原因,那么解决方案就很简单了。

将代码中判断 bool 和 int 的代码调换一下顺序即可解决问题。

1
2
3
4
5
6
7
a = True
if isinstance(a, bool):
print(f'{a} is bool!')
elif isinstance(a, int):
print(f'{a} is int!')
else:
print(f'{a} is not int or bool!')
1
True is bool!

总结

Python 历史悠久,不可避免的会有一些历史遗留问题导致我们在平时使用中会出现困惑,这时候要善于查询和质疑,通过官方文档搞懂了其背后的原理防止后面再次犯错。

参考链接

https://stackoverflow.com/questions/48675868/python-3-isinstance-bool-without-external-validator-or-type

https://docs.python.org/3/library/functions.html#isinstance

https://stackoverflow.com/questions/37888620/comparing-boolean-and-int-using-isinstance

https://www.python.org/dev/peps/pep-0285/

本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接https://blog.d77.xyz/archives/d1aeabcf.html