Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell the type of a PyObject?

Tags:

python

I am writing a C function which takes PyObject* as parameter and processes it according to the type of the data. E.g. if it is a string, an integer, a double, a list, etc.

I am looking for ways to check the type of the data, could you show me some examples?

I am using CPython 3.8.

like image 387
my_question Avatar asked Dec 30 '22 23:12

my_question


2 Answers

Most of the built-in types in Python have associated type-check functions, which you can look up in the docs. For example, PyLong_Check checks whether an object is an int (accepting subclasses), and PyLong_CheckExact does the same check, but rejecting subclasses:

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

There are also C-level equivalents of type and isinstance.

To access an object's type in C, you can use Py_TYPE(whatever). This gives you a borrowed reference to the object's type - it's equivalent to accessing the object's ob_type field. The value is of type PyTypeObject*; you can cast it to PyObject* if you need to.

Alternatively, if you need a new reference, you can use PyObject_Type(whatever). This gives you a PyObject*; you can cast it to a PyTypeObject* if you need to. (The difference in pointer types has nothing to do with the difference in reference count handling.)


There are two ways to do isinstance at C level, depending on what you actually want. The first is PyObject_IsInstance(whatever, WhateverClass), which is exactly equivalent to isinstance, including handling virtual subclasses and false __class__ attributes the way isinstance does, and handling tuples of classes as the second argument. This is often not what you really want at C level, since passing a PyObject_IsInstance test guarantees nothing about an object's memory layout.

The second is PyObject_TypeCheck(whatever, WhateverClass), which requires that whatever is a "concrete" instance of WhateverClass - the actual type of whatever has to either be WhateverClass or a "real" descendant of WhateverClass. This ensures that whatever has the actual C-level members an instance of WhateverClass is expected to have.

For example, with the following classes:

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) and PyObject_IsInstance(c, int) would both be true, but PyObject_TypeCheck(b, A) and PyObject_TypeCheck(c, int) would be false.

Type-specific type-check macros like PyLong_Check behave like PyObject_TypeCheck, not PyObject_IsInstance. They ignore virtual subclasses and false __class__ attributes.

like image 120
user2357112 supports Monica Avatar answered Jan 16 '23 23:01

user2357112 supports Monica


You should be able to use PyObject_Type

PyObject* PyObject_Type(PyObject *o)

When o is non-NULL, returns a type object corresponding to the object type of object o. On failure, raises SystemError and returns NULL. This is equivalent to the Python expression type(o). This function increments the reference count of the return value. There’s really no reason to use this function instead of the common expression o->ob_type, which returns a pointer of type PyTypeObject*, except when the incremented reference count is needed.

You might also want to look into PyObject_TypeCheck if you want to handle subclasses correctly.

like image 33
Samuel Dion-Girardeau Avatar answered Jan 16 '23 23:01

Samuel Dion-Girardeau