tl;dr 只需调用is_object_pure_python()
远在远下方定义的函数。
和ibell一样,user4815162342的权威Python 2.x 专用解决方案给我留下了深刻的印象。然而,在 Pythonic 的天堂里,一切都不是很好。
问题。问题无处不在。
该解决方案(尽管很有见地)遭受了一些简单的编辑 无法轻松解决的问题,包括:
L
Python 3.x 不支持类型后缀。诚然,微不足道的解决方法。
- 交叉解释器
is_class_instance()
实现无法解释使用__slots__
.
- CPython 特定的
is_class_instance()
实现在非 CPython 解释器(例如,pypy)下失败。
- 没有可比的实现来检测类(而不是类实例)是纯 Python 还是基于 C 的。
解决方案!解决方案无处不在!
为了解决这些问题,以下特定于 Python 3.x 的解决方案已经过重构,以便在 CPython 下选择更可靠的 CPython 特定L
实现,并回退到所有其他解释器下不太可靠的跨解释器实现,以及已被推广到检测类和类实例。__slots__
is_class_instance()
is_class_instance()
为了理智,让我们首先检测类实例:
import platform
# If the active Python interpreter is the official CPython implementation,
# prefer a more reliable CPython-specific solution guaranteed to succeed.
if platform.python_implementation() == 'CPython':
# Magic number defined by the Python codebase at "Include/object.h".
Py_TPFLAGS_HEAPTYPE = (1<<9)
def is_instance_pure_python(obj: object) -> bool:
'''
`True` if the passed object is an instance of a pure-Python class _or_
`False` if this object is an instance of a C-based class (either builtin
or defined by a C extension).
'''
return bool(type(obj).__flags__ & Py_TPFLAGS_HEAPTYPE)
# Else, fallback to a CPython-agnostic solution typically but *NOT*
# necessarily succeeding. For all real-world objects of interest, this is
# effectively successful. Edge cases exist but are suitably rare.
else:
def is_instance_pure_python(obj: object) -> bool:
'''
`True` if the passed object is an instance of a pure-Python class _or_
`False` if this object is an instance of a C-based class (either builtin
or defined by a C extension).
'''
return hasattr(obj, '__dict__') or hasattr(obj, '__slots__')
证据在圭多的布丁里
单元测试证明了令人不安的事实:
>>> class PurePythonWithDict(object): pass
>>> class PurePythonWithSlots(object): __slots__ = ()
>>> unslotted = PurePythonWithDict()
>>> slotted = PurePythonWithSlots()
>>> is_instance_pure_python(unslotted)
True
>>> is_instance_pure_python(slotted)
True
>>> is_instance_pure_python(3)
False
>>> is_instance_pure_python([3, 1, 4, 1, 5])
False
>>> import numpy
>>> is_instance_pure_python(numpy.array((3, 1, 4, 1, 5)))
False
这是否可以推广到没有实例的类?
是的,但这样做并非易事。检测一个类(而不是类实例)是纯 Python 还是基于 C 是非常困难的。为什么?因为即使是基于 C 的类也提供了该__dict__
属性。因此,hasattr(int, '__dict__') == True
。
尽管如此,如果这是一种骇人听闻的方式,就会有一种骇人听闻的意志。由于未知(可能是平庸)的原因,dir()
内置函数__dict__
从其返回的列表中删除属性名称,仅用于基于 C 的类。因此,以交叉解释器的方式检测一个类是纯 Python 还是基于 C 的,减少了迭代搜索 for 返回的dir()
列表__dict__
。为了胜利:
import platform
# If the active Python interpreter is the official CPython interpreter,
# prefer a more reliable CPython-specific solution guaranteed to succeed.
if platform.python_implementation() == 'CPython':
# Magic number defined by the Python codebase at "Include/object.h".
Py_TPFLAGS_HEAPTYPE = (1<<9)
def is_class_pure_python(cls: type) -> bool:
'''
`True` if the passed class is pure-Python _or_ `False` if this class
is C-based (either builtin or defined by a C extension).
'''
return bool(cls.__flags__ & Py_TPFLAGS_HEAPTYPE)
# Else, fallback to a CPython-agnostic solution typically but *NOT*
# necessarily succeeding. For all real-world objects of interest, this is
# effectively successful. Edge cases exist but are suitably rare.
else:
def is_class_pure_python(cls: type) -> bool:
'''
`True` if the passed class is pure-Python _or_ `False` if this class
is C-based (either builtin or defined by a C extension).
'''
return '__dict__' in dir(cls) or hasattr(cls, '__slots__')
更多证明。更多布丁。
更多测试驱动的真实性:
>>> class PurePythonWithDict(object): pass
>>> class PurePythonWithSlots(object): __slots__ = ()
>>> is_class_pure_python(PurePythonWithDict)
True
>>> is_class_pure_python(PurePythonWithSlots)
True
>>> is_class_pure_python(int)
False
>>> is_class_pure_python(list)
False
>>> import numpy
>>> is_class_pure_python(numpy.ndarray)
False
这就是她写的
为了一般性,让我们将上面定义的低级函数统一为两个高级函数,支持所有可能的 Python 解释器下的所有可能类型:
def is_object_pure_python(obj: object) -> bool:
'''
`True` if the passed object is either a pure-Python class or instance of
such a class _or_ `False` if this object is either a C-based class
(builtin or defined by a C extension) or instance of such a class.
'''
if isinstance(obj, type):
return is_class_pure_python(obj)
else:
return is_instance_pure_python(obj)
def is_object_c_based(obj: object) -> bool:
'''
`True` if the passed object is either a C-based class (builtin or
defined by a C extension) or instance of such a class _or_ `False` if this
object is either a pure-Python class or instance of such a class.
'''
return not is_object_pure_python(obj)
看哪!纯 Python。