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