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.
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.
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 returnsNULL. This is equivalent to the Python expressiontype(o). This function increments the reference count of the return value. There’s really no reason to use this function instead of the common expressiono->ob_type, which returns a pointer of typePyTypeObject*, except when the incremented reference count is needed.
You might also want to look into PyObject_TypeCheck if you want to handle subclasses correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With