在运行时蟒确定对象是否是类(新旧型)实例(In python at runtime determin

2019-06-27 17:00发布

我想写使用h5py模块长期储存深度嵌套组类,属性,绑定方法等,以一个HDF5文件。 我真的很接近。 我有我似乎无法化解的唯一的一招是一种以编程方式,在运行时,找出一种方法来确定,如果事情是一个类的实例的类型,而不是一个列表,INT,等我需要递归到类的实例,但显然不应该递归到一个int,float等这需要两个旧式和新式的类工作。 我研究的是不工作的东西/我不能让工作:

使用检查模块

>>> class R(object): pass
...
>>> _R = R()
>>> import inspect
>>> inspect.isclass(_R)
False
>>> inspect.isclass(R)
True

这是没有帮助的,我需要像函数inspect.isclassinstance(_R)返回True

使用类型模块

如果你使用老式类,有一个叫做InstanceType类型匹配旧式的类的实例,如下面的代码

>>> import types
>>> class R(): pass #old-style class
...
>>> _R = R()
>>> import types
>>> type(_R) is types.InstanceType
True
>>> class R(object): pass #new-style class
...
>>> _R = R()
>>> type(_R) is types.InstanceType
False

但是如果你使用新式的类中有没有相应的类型types

Answer 1:

虽然海报可能最有可能需要重新考虑他的设计,在某些情况下有正当需要在Python与创建类的实例内置/扩展类型,在C创建的实例,并区分class的语句。 虽然两者都是两类,后者是一个类型的类别,因为它们的类型结构在运行时分配的CPython的内部称之为“堆型”。 这蟒蛇继续区分它们中可以看到__repr__输出:

>>> int       # "type"
<type 'int'>
>>> class X(object): pass
... 
>>> X         # "class"
<class '__main__.X'>

__repr__区别正是通过检查类型是否是一个堆型实现。

根据应用的具体需求,一个is_class_instance功能,可以通过以下方式之一来实现:

# Built-in types such as int or object do not have __dict__ by
# default. __dict__ is normally obtained by inheriting from a
# dictless type using the class statement.  Checking for the
# existence of __dict__ is an indication of a class instance.
#
# Caveat: a built-in or extension type can still request instance
# dicts using tp_dictoffset, and a class can suppress it with
# __slots__.
def is_class_instance(o):
    return hasattr(o, '__dict__')

# A reliable approach, but one that is also more dependent
# on the CPython implementation.
Py_TPFLAGS_HEAPTYPE = (1<<9)       # Include/object.h
def is_class_instance(o):
    return bool(type(o).__flags__ & Py_TPFLAGS_HEAPTYPE)

编辑

下面是该函数的第二个版本的解释。 这真的测试的类型是否是一个“堆型”使用CPython的内部使用其自己的目的相同的测试。 这就确保了它将始终为(“类”)和False的非堆类型的实例堆类型的实例(“类型”,但也老式类,这是很容易解决)返回True。 它通过检查是否所述tp_flags构件 C级的PyTypeObject结构具有Py_TPFLAGS_HEAPTYPE位设置。 实施薄弱的部分是它的硬编码的价值Py_TPFLAGS_HEAPTYPE不断到当前观测值。 (因为不断不是由符号名暴露到Python这是必需的。)虽然理论上这个常数可能会改变,这是极不可能在实践中发生,因为这样的改变会无端破坏现有的扩展模块的ABI。 在看的定义Py_TPFLAGS常量中Include/object.h ,显而易见的是,新的,而不会干扰旧的被小心地加入。 另一个缺点是,这种代码有机会零在非CPython的实现,如Jython或IronPython的运行。



Answer 2:

感谢@ user4815162342,我已经能够得到这个工作。 这里有一个稍微修改后的版本,将返回True为旧式和新型类的实例:

#Added the check for old-style class
Py_TPFLAGS_HEAPTYPE = (1L<<9)       # Include/object.h
def is_class_instance(o):
    import types
    return (bool(type(o).__flags__ & Py_TPFLAGS_HEAPTYPE) 
            or type(o) is types.InstanceType)


Answer 3:

TL;博士 只需调用is_object_pure_python()远,远低于定义的函数。

像ibell ,我尽职尽责地留下深刻的印象user4815162342的权威的Python 2.x的具体的解决方案 。 一切都没有得到很好的Python的天堂,但是。

问题。 问题无处不在。

该解决方案(虽然有见地)遭受了一点一点腐烂通过简单的编辑,包括平凡解析:

  • L类型后缀是下的Python 3.x的不支持的 诚然,平凡解析。
  • 横解释is_class_instance()执行没有考虑具有优化的纯Python类__slots__
  • CPython的特定is_class_instance()在非CPython的解释(例如,pypy)执行失败。
  • 用于检测是否没有类似的实现(而不是类实例 )是纯Python或基于℃。

解决方案! 解决方案无处不在!

为了解决这些问题,以下Python特定3.x的溶液滴L ,检测__slots__ ,已重构以选择更加可靠的CPython特异性is_class_instance() CPython的正在执行,回退到较少可靠的跨解释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或基于下在横解释的方式减少了迭代地搜索由返回的列表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。



文章来源: In python at runtime determine if an object is a class (old and new type) instance
标签: python class