在运行时的python中,确定对象是否为类(旧类型和新类型)实例

在运行时的python中,确定对象是否为类(旧类型和新类型)实例

问题描述:

我正在尝试使用h5py模块将一组深层嵌套的类,属性,绑定方法等写入HDF5文件中以进行长期存储。我真的很亲近我唯一无法解决的问题是在运行时以编程方式找出一种确定某项是否是类实例类型而不是列表,int等的方法。我需要递归到类实例,但显然不应该递归为int,float等。这对于新旧类均适用。我研究过的东西无效/无法工作:

I am trying to write a deeply nested set of classes, attributes, bound methods, etc. to a HDF5 file using the h5py module for long-term storage. I am really close. The only wrinkle I have that I can't seem to resolve is to programmatically, at run-time, figure out a way to determine if something is a class instance type, rather than a list, int, etc. I need to recurse into the class instance, but obviously shouldn't recurse into an int, float, etc. This needs to work for both old- and new-style classes. Things that I researched that don't work/ I can't get to work:

使用检查模块

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

这没有帮助,我需要一个函数例如 inspect.isclassinstance(_R)即可返回 True

This isn't helpful, I need a function like inspect.isclassinstance(_R) to return True

使用类型模块

如果您使用旧式类,则存在一个名为InstanceType的类型,该类型将旧式类的实例与在下面的代码中

If you use old-style classes, there is a type called InstanceType that matches instances of old-style classes as in the code below

>>> 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

But if you use new-style classes there is no corresponding type in types

中,没有相应的类型可能需要重新考虑他的设计,在某些情况下,确实有必要区分使用C创建的内置/扩展类型实例和使用 class $用Python创建的类实例。 c $ c>语句。虽然两者都是类型,但后者是CPython内部称为堆类型的类型类别,因为它们的类型结构是在运行时分配的。可以在 __ repr __ 输出中看到python继续区分它们:

While the poster might most likely need to rethink his design, in some cases there is a legitimate need to distinguish between instances of built-in/extension types, created in C, and instances of classes created in Python with the class statement. While both are types, the latter are a category of types that CPython internally calls "heap types" because their type structures are allocated at run-time. That python continues to distinguish them can be seen in __repr__ output:

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

__ repr __ 的区别是通过检查来实现的

The __repr__ distinction is implemented exactly by checking whether the type is a heap type.

根据应用程序的确切需求,可以使用 is_class_instance 函数以下列方式之一实现:

Depending on the exact needs of the application, an is_class_instance function can be implemented in one of the following ways:

# 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内部出于其自身目的使用的相同测试来测试该类型是否为堆类型。这样可以确保对于堆类型(类)的实例始终返回True,对于非堆类型( types,还包括易于修复的旧式类)实例始终返回False。通过检查 tp_flags C级 成员 > PyTypeObject 结构设置了 Py_TPFLAGS_HEAPTYPE 位。该实现的弱点在于它将 Py_TPFLAGS_HEAPTYPE 常数的值硬编码为当前观察到的值。 (这是必需的,因为该常量不会以符号名称显示给Python。)尽管从理论上讲该常量可以更改,但实际上不太可能发生,因为这样的更改会无意破坏现有扩展模块的ABI。查看 Py_TPFLAGS的定义在 Include / object.h 中的code>常量中,很明显,正在小心地添加新的常量而不打扰旧的常量。另一个弱点是该代码在非CPython实现(如Jython或IronPython)上运行的机会为零。

Here is an explanation of the second version of the function. It really tests whether the type is a "heap type" using the same test that CPython uses internally for its own purposes. That ensures that it will always return True for instances of heap types ("classes") and False for instances of non-heap-types ("types", but also old-style classes, which is easy to fix). It does that by checking whether the tp_flags member of the C-level PyTypeObject structure has the Py_TPFLAGS_HEAPTYPE bit set. The weak part of the implementation is that it hardcodes the value of the Py_TPFLAGS_HEAPTYPE constant to the currently observed value. (This is necessary because the constant is not exposed to Python by a symbolic name.) While in theory this constant could change, it is highly unlikely to happen in practice because such a change would gratuitously break the ABI of existing extension modules. Looking at the definitions of Py_TPFLAGS constants in Include/object.h, it is apparent that new ones are being carefully added without disturbing the old ones. Another weakness is that this code has zero chance running on a non-CPython implementation, such as Jython or IronPython.