python的自省机制也是其一大彪悍的特性,对于任何一个对象,我们都可以准确的获取其类型。

print(type(123))
print(type(""))
print(type(str))
print(type(map))
"""
<class 'int'>
<class 'str'>
<class 'type'>
<class 'type'>
"""

但是这种获取方式有一个缺陷,比如说:

class A:
    pass


class B(A):
    pass


b = B()
print(type(b))  # <class '__main__.B'>

这里B继承自A,但是b是由B这个类实例化出来的,因此如果type(b)的话,打印的就是B这个类。但是我要怎么判断b和A之间的关系呢?

class A:
    pass


class B(A):
    pass


b = B()
print(isinstance(b, A))  # True

显然可以使用isinstance,如果A是B的父类,那么b不仅是B的实例对象,同时也是A的实例对象。但是这里问题就来了,这个isinstance的检测机制是什么?我们可以再来看一个例子。

from collections.abc import Iterable

s = ""
l = []
d = {}

print(isinstance(s, Iterable))
print(isinstance(l, Iterable))
print(isinstance(d, Iterable))
"""
True
True
True
"""

我们发现字符串对象、列表对象、字典对象都继承自Iterable,因为它们都是可迭代的的。因此这个isinstance比type的范围要更广泛一些,type得到的就是具体实例化该对象类,而isinstance则貌似是要求满足某种特定的要件就可以。我们来看一下源码。

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__方法,其实关键就在这里。

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

以上です,希望各位能明白isinstance的机制。

01-06 12:54
查看更多