目录

type() 和 isinstance() 的异同

前言

本文介绍一下 Python 3 中的 type() 函数和 isinstance() 函数的作用,重点介绍它们的区别。

注意

本文中的命令行示范,都是基于以下环境:

  • 操作系统: CentOS 7
  • Python: 3.7.0 3.7.9 (更新)

 

type() 和 isinstance() 的作用

Python 是一个动态类型语言(Dynamically Typed Language)(注意,不要和动态语言混淆),所谓动态类型语言,是指在程序运行期间才去做数据类型检查的语言。用动态类型语言编程时,不需要给变量指定数据类型。一个变量,一开始被创建为一个字符串类型,之后可以重新被赋值为一个整形。对于这个变量名本身而言,它只是一个不带类型的标记、一个引用(reference),指向一个具体的对象。既然变量名只是一个不带类型的标记,所以它可以被赋值为任何类型:

1
2
3
4
name = 'Jack'
name = 'Mike'
name = ('John', 29, 'USA')
name = None

既然一个变量名本身指向的对象可以变化,那么在很多场合就需要检查变量的类型,或者说检查这个变量指向的对象的类型。比如,你定义一个函数,它的参数既可以是一个列表类型,也可以是这个列表元素的类型,那么在函数内,你就需要检查参数类型。

为了检查对象的类型,我们可以用type()或者isinstance()这两个 Python 自带的函数:

1
2
3
4
5
>>> name = 'Jack'
>>> type(name) is str
True
>>> isinstance(name, str)
True

Python 中,所有东西都是对象,所以任何类型本身也是对象,它的类型就是type(这里不是 type 方法,而是 type 这个类):

1
2
3
4
>>> type(str)
<class 'type'>
>>> isinstance(str, type)
True

 

type() 和 isinstance() 区别

type()函数和isinstance()函数虽然都可以用来检查对象的类型,但两者存在本质的区别:

  • type只返回对象的类型,即对象所属的类;
  • isinstance可以检查一个给定的对象(第一个参数)是否是:
    • 一个类(第二个参数)的实例 ?
    • 或者,是否是一个类(第二个参数)的子类的实例 ?

在实际使用中,两者的差异意味着什么?我们来看一个例子,假设我们自定义了一个类,这个类继承自字典,重载了__str__函数:

1
2
3
class A(dict):
  def __str__(self):
    return "It's a object of A"

然后我们定义了一个 A 类型的对象,并分别检查它是否是 A 类型及字典类型:

1
2
3
4
5
6
7
8
9
>>> test = A()
>>> type(test) is A
True
>>> type(test) is dict
False
>>> isinstance(test, A)
True
>>> isinstance(test, dict)
True

可以看到,检查 test 对象是否是 A 类型时,两个函数的结果相同,都是 True,但检查 test 对象是否是 dict 类型时,type的结果是 False,isinstance的结果是 True。这是因为isinstance方法不仅检查对象所属的类,也会检查它继承关系上的所有的类。

通俗的语言更容易理解,假设有一个类叫 “学生”,它有多个子类,表示不同的院系的学生,比如:“计算机系”,“英语系”,“化学系” 等,现在有个学生叫 “张三”,它是 “计算机系” 这个类的对象:type方法是来检查 “张三” 是哪个系的,isinstance方法可以检查 “张三” 是不是某个系,或者是不是 “学生”。显然,isinstance(“张三”, “学生”)也是成立的。

另外,isinstance还支持检查是否是多个类型里的一种,即第二个参数可以是一个元祖:

1
2
3
4
>>> isinstance(test, (dict, str))
True
>>> isinstance(test, (list, str))
False

 

性能比较

typeisinstance方法在执行性能上也略有差异,我们可以用 Python 的timeit模块来测试一下两者的性能:

1
2
3
4
5
$ python -m timeit -s "test = 'hello'" "type(test) is int"
5000000 loops, best of 5: 72.4 nsec per loop

$ python -m timeit -s "test = 'hello'" "isinstance(test, int)"
5000000 loops, best of 5: 85.6 nsec per loop

这里给 test 变量赋上一个字符串,再检查它的类型。isinstance花的时间比type略多,其实,理解了两个函数的原理也就能理解性能差异:type只要检查对象所属的类,isinstance要沿着类的继承关系往上遍历,如果检查的类型不是对象所属的类型,那么isinstance方法就会花更多的时间。

需要注意的是,使用type方法时,有些人会用==来判断类型,它虽然也可以工作,但不推荐。原因有两点

  • ==用于比较两个对象的内容是否一致,而is是比较两个变量是否指向同一个对象。type返回的是类对象,使用is即可完成比较。

  • ==由于比较的是对象内容,所以要比is执行得慢:

    1
    2
    
    $ python -m timeit -s "test = 'hello'" "type(test) == int"
    5000000 loops, best of 5: 82.6 nsec per loop
    

 

结论

isinstance通常是检查类型的更好方式,它会考虑类的继承关系。在 Python 中,程序员通常要检查的是一个对象是否具有诸如字符串的某个行为,而不是检查它是否是一个准确的字符串类型。所以这种情况用 isinstance(object, str) 更合适。

但另一方面,当你想精确地检查一个对象是某种类型的时候,那就适合用type方法。并且记住,用is而不是用==来进行比较。


- 全文完 -

相关文章

「 您的赞赏是激励我创作和分享的最大动力! 」