如何确定 PyObject 的类型?

4
我正在编写一个 C 函数,它以 PyObject* 为参数并根据数据类型进行处理。例如,如果它是一个字符串、整数、双精度浮点数、列表等等。
我正在寻找一些检查数据类型的方法,您能给我展示一些例子吗?
我使用的是 CPython 3.8。

你能在Python中使用type()吗? - Cresht
2个回答

7

Python中的大多数内置类型都有相关的类型检查函数,你可以在文档中查找。例如,PyLong_Check 检查一个对象是否为 int(包括子类),而 PyLong_CheckExact 也执行相同的检查,但会拒绝子类:

if (PyLong_Check(whatever)) {
    // do int stuff
}

还有C级别的typeisinstance的相当版本。
要在C中访问对象的类型,可以使用Py_TYPE(whatever)。这会给你一个借用引用到对象类型-它相当于访问对象的ob_type字段。值的类型为PyTypeObject*;如果需要,可以将其转换为PyObject*
此外,如果需要新引用,可以使用PyObject_Type(whatever)。这会给你一个PyObject*;如果需要,可以将其转换为PyTypeObject*。(指针类型的差异与引用计数处理的差异无关)
在C级别上执行isinstance有两种方法,具体取决于您实际想要的内容。第一种是PyObject_IsInstance(whatever, WhateverClass),它与isinstance完全相同,包括处理虚拟子类和错误的__class__属性,以及将元组作为第二个参数的处理方法。这通常不是您在C级别上真正想要的,因为通过PyObject_IsInstance测试传递并不能保证对象的内存布局。
第二种方法是PyObject_TypeCheck(whatever, WhateverClass),它要求whateverWhateverClass的“具体”实例-whatever的实际类型必须是WhateverClass或者是一个“真正”的WhateverClass后代。这确保了whatever具有预期具体的C级别成员。
例如,对于以下类:
import abc

class A(metaclass=abc.ABCMeta):
    pass

class B: pass

A.register(B)

class C:
    __class__ = int

b = B()
c = C()

PyObject_IsInstance(b, A)PyObject_IsInstance(c, int) 都将返回 true,但是 PyObject_TypeCheck(b, A)PyObject_TypeCheck(c, int) 将返回 false。

PyLong_Check 这样的特定类型检查宏行为类似于 PyObject_TypeCheck,而不是 PyObject_IsInstance。它们忽略虚拟子类和错误的 __class__ 属性。


PyObject_IsInstance 如果有元类,是否会回调到 Python 级别的代码?如果我理解正确,您可以使用元类在 Python 中实现自定义实现。这与 PyObject_TypeCheck 有什么不同吗? - flakes
1
@flakes:PyObject_IsInstance将调用__instancecheck__,这可能会调用Python级别的代码。PyObject_TypeCheck不会调用Python级别的代码。 - user2357112

3

您应该能够使用PyObject_Type

PyObject* PyObject_Type(PyObject *o)

oNULL时,返回与对象o的对象类型对应的类型对象。在失败时,引发SystemError并返回NULL。这等效于Python表达式type(o)。此函数增加了返回值的引用计数。除非需要增加引用计数,否则没有理由使用此函数而不是常见的表达式o->ob_type,后者返回PyTypeObject*类型的指针。

如果您想正确处理子类,您可能还需要查看PyObject_TypeCheck


很好的回答。在什么情况下,您想确保引用计数增加?这是为了防止类本身被垃圾回收吗? - flakes
1
@flakes:你需要增加任何其他对象的引用计数的确切情况相同:当你需要一个新的引用,独立于原始引用时。例如,如果你想从你正在编写的函数中返回一个引用,通常需要确保它是一个新的引用,而不是一个借用的引用。 - user2357112

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接