性能实际上只是与ABCmeta的
__instancecheck__支票集合联系在一起,这称为
isinstance。
最重要的是,这里看到的性能不佳不是由于缺少一些优化,而是
isinstanceJim提到的抽象基类是Python级别的操作的结果。正和负的结果都会被缓存,但是即使有缓存的结果,您也要在每个循环中花几微秒的时间来遍历
__instancecheck__ABCmeta类方法中的条件。
一个例子
考虑一些不同的空结构。
>>> d = dict; l = list(); s = pd.Series()>>> %timeit isinstance(d, collections.abc.Mapping)100000 loops, best of 3: 1.99 µs per loop>>> %timeit isinstance(l, collections.abc.Mapping)100000 loops, best of 3: 3.16 µs per loop # caching happening>>> %timeit isinstance(s, collections.abc.Mapping)100000 loops, best of 3: 3.26 µs per loop # caching happening
我们可以看到性能差异-是什么原因造成的?
为了一个命令
>>> %lprun -f abc.ABCmeta.__instancecheck__ isinstance(dict(), collections.abc.Mapping)Timer unit: 6.84247e-07 sTotal time: 1.71062e-05 sLine # Hits Time Per Hit % Time Line Contents============================================================== 178 def __instancecheck__(cls, instance): 179 """Override for isinstance(instance, cls).""" 180 # Inline the cache checking 181 1 7 7.0 28.0 subclass = instance.__class__ 182 116 16.0 64.0 if subclass in cls._abc_cache: 183 1 2 2.0 8.0 return True 184 subtype = type(instance) 185 if subtype is subclass: 186if (cls._abc_negative_cache_version == 187 ABCmeta._abc_invalidation_counter and 188 subclass in cls._abc_negative_cache): 189 return False 190# Fall back to the subclass check. 191return cls.__subclasscheck__(subclass) 192 return any(cls.__subclasscheck__(c) for c in {subclass, subtype})清单
>>> %lprun -f abc.ABCmeta.__instancecheck__ isinstance(list(), collections.abc.Mapping)Timer unit: 6.84247e-07 sTotal time: 3.07911e-05 sLine # Hits Time Per Hit % Time Line Contents============================================================== 178 def __instancecheck__(cls, instance): 179 """Override for isinstance(instance, cls).""" 180 # Inline the cache checking 181 1 7 7.0 15.6 subclass = instance.__class__ 182 117 17.0 37.8 if subclass in cls._abc_cache: 183return True 184 1 2 2.0 4.4 subtype = type(instance) 185 1 2 2.0 4.4 if subtype is subclass: 186 1 3 3.0 6.7 if (cls._abc_negative_cache_version == 187 1 2 2.0 4.4 ABCmeta._abc_invalidation_counter and 188 110 10.0 22.2 subclass in cls._abc_negative_cache): 189 1 2 2.0 4.4 return False 190# Fall back to the subclass check. 191return cls.__subclasscheck__(subclass) 192 return any(cls.__subclasscheck__(c) for c in {subclass, subtype})我们可以看到,对于dict,映射抽象类的
_abc_cache
>>> list(collections.abc.Mapping._abc_cache)[dict]
包括我们的字典,因此检查会尽早短路。对于列表,显然不会命中正缓存,但是“映射”
_abc_negative_cache包含列表类型
>>> list(collections.abc.Mapping._abc_negative_cache)[type, list, generator, pandas.core.series.Series, itertools.chain, int, map]
以及现在的pd.Series类型,这是因为使用
isinstance多次调用了
%timeit。在我们没有命中负缓存的情况下(例如Series的第一次迭代),Python会使用带有
cls.__subclasscheck__(subclass)
其可以是 远
慢,诉诸子类钩和递归子类检查这里看到,那么高速缓存用于随后的加速比的结果。



