目录
前言
isinstance相信大家都已经很熟悉了,对于他的用法和一些基本的操作,我们再做一个介绍。
但是我今天最想和大家将的其实是isinstance(object, classinfo) 中的classinfo都有哪些类型,int、str、dict、bool、list等这些基本类型都是一些最基本的,本文将告诉大家一些常用的classinfo,以及isinstance它的内部到底是怎样一个判断机制。
一、isinstance的基本使用
1、语法
isinstance(object, classinfo)
- object -- 实例对象。
- classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。
注意到lclassinfo这个参数的描述,可以是直接或间接类名,这个其实是因为isinstance的判断机制决定的,后面回过头来看你就明白了。
返回值:如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。。
2、基本用法
>>>a = 2
>>> isinstance (a,int)
True
>>> isinstance (a,str)
False
>>> isinstance (a,(str,int,list)) # 是元组中的一个返回 True
True
isinstance() 与 type() 区别:
-
type() 不会认为子类是一种父类类型,不考虑继承关系。
-
isinstance() 会认为子类是一种父类类型,考虑继承关系。
如果要判断两个类型是否相同推荐使用 isinstance()。
class A:
pass
class B(A):
pass
isinstance(A(), A) # returns True
type(A()) == A # returns True
isinstance(B(), A) # returns True
type(B()) == A # returns False
二、常用的classinfo
我们常见的classinfo有
int,float,bool,complex,str(字符串),list,dict(字典),set,tuple
但是,在实际工程中,其实这些用的并不多。用的比较多的首先是 判断类的实例,这种就不介绍了。另外工程中用的还比较多的就是typing模块中的一套数据类型。关于tying模块,又不熟悉的小伙伴可以看我这篇文章《python高级模块包之typing》。
我列出typing中我们常用的classinfo,当然一下也只是一些常用的,还有很多我没有列出来。并且,有兴趣的人可以去看一下typing的源码,它里里面的容器类型其实都是从collections导入的。所以说下面的数据类型是typing和collections的合集也是正确的。
Dict, Tuple, List
Iterable,Iterator,Generator,Hashable,Sized,Mapping,Sequence,Set,Dict
Coroutine,Awaitable
除了上面,我还要提一个数据类型,Number。在我们验证一个类型是不是数字时,可以使用下面两种方法:
>>> a = 1.1
>>> b = 1
>>> isinstance(a, (int, float))
True
>>> isinstance(b, (int, float))
True
#*********************************#
>>> from numbers import Number
>>> isinstance(a, Number)
True
>>> isinstance(b, Number)
True
当然,numbers模块中还有其他的数据类型,如Complex(复数类型),Intergral(整数类型)。都可以用在isinstance上。
那么,大家有没有想过,为什么我去判断一个数是不是整数,classinfo可以有int、Intergral甚至更多个classinfo呢?其实答案就在我们前面提到的,classinfo可以是直接或者间接类名。
三、isinstance的内部判断机制
这里是参考的一篇网上的文章,出处:http://www.manongjc.com/detail/13-feratkzldglpqji.html
我觉得,需要先铺垫一个概念,就是-鸭子类型。不会的可以再去理解一下。
我们通常说的,这个类型是不是iterable,是不是gengerator,其实就是看它内部是否实现了某一个或多个指定的方法。只要某个类它实现了这个方法,那他就符合对应的类型。这其实就是鸭子类型的使用。
其实isinstance正是使用了鸭子类型这一点,我们来看下面的分析。
type得到的就是具体实例化该对象类,而isinstance则貌似是要求满足某种特定的要件就可以。我们来看一下Iterable源码。
class Iterable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
return _check_methods(C, "__iter__")
return NotImplemented
class Sized(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __len__(self):
return 0
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
return _check_methods(C, "__len__")
return NotImplemented
class Container(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __contains__(self, x):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
return _check_methods(C, "__contains__")
return NotImplemented
我们注意到这些都是一个抽象基类,里面定义了一个__subclasshook__
方法,其实关键就在这里。
我们只要满足__subclasshook__的条件,instance就会判断他们是相同的类型。
因此我们自己实现一个类,测试下
from abc import ABCMeta
class A(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, C):
# 当我们使用isinstance(obj, A)的时候
# 那么实例化obj所对应类,就会作为参数传递给C
# 我们可以自定义,比如这里
# 如果C有fuck这个属性,那么就认为obj也是A的实例
return hasattr(C, "fuck")
class S:
def fuck(self):
pass
s = S()
print(isinstance(s, A)) # True
# 我们注意到S这个类没有继承任何东西,但是它确是A的实例
# 因为我们自己实现了抽象基类
# 不仅如此,此时的S这个类还是A的子类
print(issubclass(S, A)) # True
这其实也验证了instance的鸭子类型的思想。
所以我们有时候会见到,一个类它即是可迭代的,还是Sized等等特性