I understand that multiplying
a ctype
with an integer
is declaring an array.
For example, ctypes.c_int * 4
is an array of 4 integers.
Then what is py_object
?
Please help me understand how does ctypes.c_int
differs from ctypes.py_object?
from ctypes import c_int, py_object
# Array of (three) integers initialized such that
# a[0] = 1, a[1] = 2, a[2] = 3.
a = (c_int * 3)(1, 2, 3)
# Array of (three) arbitrary Python objects initialized
# such that a[0] = "Pizza", a[1] = 5, a[2] = 5.813.
b = (py_object * 3)("Pizza", 5, 5.813)
print(a[:]) # -> [1, 2, 3]
print(b[:]) # -> ['Pizza', 5, 5.813]
Trying to execute
a = (c_int * 3)("Pizza", 5, 5.813)
will generate a TypeError: an integer is required (got type str)
.
This is because we have specified that we want an array of three elements, all of which are integers, but we have attempted to include a string inside the array as well.
Arrays, unlike lists, require that all elements a[0], a[1], ... , a[n]
are of the same type.
By specifying py_object
instead of c_int
, you are essentially permitted to have an array of arbitrary Python objects, as opposed to only integers.
Strictly speaking (in CPython), internally, the py_object
way gives you an array of pointers to the PyObject
datatype. Internally, all object types are extensions of PyObject
.
So the type of all the elements of the b
array is pointer to PyObject
. A pointer is nothing more than a memory address. So every element of the b
array is just a memory address pointing to some arbitrary object in Python. Internally, integer, floats, lists, dictionaries, and so on are all PyObject
s so an array with elements of type py_object
allows for its elements to be memory addresses that refer to essentially any kind of object in Python (lists, tuples, integers, floats, and so on).
When we print b[0]
, for example, Python gives us 'Pizza'
as output. Internally, Python uses the memory address of the string object and returns the string for us.
Pointers / memory addresses are used frequently in C and in the CPython source code. Python essentially hides memory addresses from us as it is a high-level language, abstracting away from low-level concepts such as pointers.
I hope this helps!