TLDR:
if isinstance(x, int):
除非您有理由不使用,否则请使用。
- 如果
if type(x) is int:
您需要精确的类型相等而不需要其他任何东西,请使用。
- 如果
try: ix = int(x)
您可以转换为目标类型,请使用。
Python 中的类型检查有一个很大的“取决于”。处理类型的方法有很多种,各有利弊。随着 Python3,出现了更多。
显式类型相等
类型是一流的对象,您可以像对待任何其他值一样对待它们。因此,如果您希望某物的类型等于int
,只需对其进行测试:
if type(x) is int:
这是最严格的测试类型:它需要精确的类型相等。通常,这不是您想要的:
- 它排除了替代类型:a
float
是无效的,即使它int
在许多方面表现得像 an。
- 它排除了子类和抽象类型:打印漂亮的
int
子类或enum
将被拒绝,即使它们在逻辑上是整数。
- 这严重限制了可移植性:Python2 字符串可以是
str
or unicode
,而整数可以是 int
or long
。
请注意,显式类型相等可用于低级操作:
- 某些类型不能被子类化,例如
slice
. 一个明确的检查在这里更明确。
- 一些低级操作,例如序列化或 C-API,需要特定类型。
变体
也可以对__class__
属性进行比较:
if x.__class__ is int:
请注意,如果一个类定义了一个__class__
属性,这与type(x)
.
当有多个类要检查时,使用 adict
来调度动作比显式检查更具可扩展性并且可以更快(≥5-10 种类型)。这对于转换和序列化特别有用:
dispatch_dict = {float: round, str: int, int: lambda x: x}
def convert(x):
converter = self.dispatch_dict[type(x)] # lookup callable based on type
return converter(x)
显式类型的实例检查
惯用类型测试使用isinstance
内置:
if isinstance(x, int):
此检查既准确又高效。这通常是人们想要检查类型的内容:
- 它正确处理子类型。一个漂亮的打印
int
子类仍然可以通过这个测试。
- 它允许一次检查多种类型。在 Python2 中,doing
isinstance(x, (int, long))
会为您提供所有内置整数。
最重要的是,大多数时候缺点可以忽略不计:
- 它仍然接受行为怪异的时髦子类。由于任何东西都可以以奇怪的方式表现,因此防范是徒劳的。
- 它很容易过于严格:许多人检查
isinstance(x, list)
任何序列(例如tuple
)甚至可迭代(例如 a generator
)何时也可以。与脚本或应用程序相比,通用库更需要关注这一点。
变体
如果您已经有一个类型,issubclass
则行为相同:
if issubclass(x_type, int):
抽象类型的实例检查
Python 有一个抽象基类的概念。粗略地说,这些表达了类型的含义,而不是它们的层次结构:
if isinstance(x, numbers.Real): # accept anything you can sum up like a number
换句话说, type(x) 不一定继承自它,numbers.Real
但必须表现得像它。尽管如此,这是一个非常复杂和困难的概念:
- 如果您正在寻找基本类型,这通常是矫枉过正。
int
大多数时候,整数只是一个。
- 来自其他语言的人经常混淆它的概念。
- 将它与例如 C++ 区分开来,重点是抽象基类而不是抽象基类。
- ABC 可以像 Java 接口一样使用,但仍可能具有具体功能。
但是,它对于泛型库和抽象非常有用。
- 许多函数/算法不需要显式类型,只需要它们的行为。
- 如果您只需要按键查找内容,则将您
dict
限制为特定的内存类型。相比之下,collections.abc.Mapping
还包括数据库包装器、大型磁盘支持的字典、惰性容器,... - 和dict
.
- 它允许表达部分类型约束。
- 没有严格的基类型实现迭代。但是如果你检查对象
collections.abc.Iterable
,它们都在一个for
循环中工作。
- 它允许创建显示为相同抽象类型的单独、优化的实现。
虽然一次性脚本通常不需要它,但我强烈建议将它用于除了几个 python 版本之外的任何东西。
暂定转换
处理类型的惯用方式不是测试它们,而是假设它们是兼容的。如果您已经预料到输入中有一些错误的类型,只需跳过所有不兼容的内容:
try:
ix = int(x)
except (ValueError, TypeError):
continue # not compatible with int, try the next one
else:
a.append(ix)
这实际上不是类型检查,但通常具有相同的目的。
- 它保证您的输出中有预期的类型。
- 它在转换错误类型方面有一些有限的回旋余地,例如专门转换
float
为int
.
- 它可以在您不知道哪些类型符合
int
.
主要的缺点是它是一个显式的转换。
- 您可以默默地接受“错误”值,例如转换
str
包含文字的 a。
- 它不必要地转换甚至足够好的类型,例如当你只
float
需要int
数字时。
转换是某些特定用例的有效工具。如果您大致了解您的输入是什么,并且必须对您的输出做出保证,则效果最好。
函数调度
有时类型检查的目标只是选择一个合适的函数。在这种情况下,函数调度例如functools.singledispatch
允许针对特定类型专门化函数实现:
@singledispatch
def append_int(value, sequence):
return
@append_int.register
def _(value: int, sequence):
sequence.append(value)
这是isinstance
和dict
调度的结合。它对于大型应用程序最有用:
- 无论分派类型的数量如何,它都会使使用站点保持较小。
- 它允许以后注册其他类型的特化,即使在其他模块中也是如此。
尽管如此,它并非没有缺点:
- 许多 Python 程序员起源于函数式和强类型语言,不熟悉单调度甚至多调度。
- 调度需要单独的功能,因此不适合在使用现场定义。
- 创建函数和“预热”调度缓存需要显着的运行时开销。调度函数应该定义一次并经常重复使用。
- 即使是预热的调度表也比手写的 if/else 或
dict
查找要慢。
控制输入
最好的做法是确保您一开始就不必检查类型。这是一个元主题,因为它在很大程度上取决于用例。
在这里,来源不somelist
应该将非数字放入其中。