Reputation: 6513
I’m trying to write a ctypes structure so that it can be easily converted into a numpy array and used for assignments. Here is a simple example that shows the issue:
from ctypes import Structure, c_double
import numpy as np
class Vec3d:
x = 1
y = 2
z = 3
def __array__(self, dtype):
return np.array([self.x, self.y, self.z])
class Vec3dS(Structure):
_fields_ = [("x", c_double), ("y", c_double), ("z", c_double)]
def __array__(self, dtype):
return np.array([self.x, self.y, self.z])
v = Vec3d()
vs = Vec3dS(1,2,3)
n = np.zeros((2,3))
n[0] = v
n[1] = vs
The first assignment n[0]=v
works but not the second one n[1]=vs
. Numpy seems to be able to convert v
to an numpy array but the assignment ultimately fails because the array has the wrong dtype:
TypeError: Cannot cast array data from dtype([('x', '<f8'), ('y', '<f8'), ('z', '<f8')]) to dtype('float64').
It’s the same dtype as if I were using
np.array(vs)
Why does implementing __array__
(I also tried __array_interface__
) not work when using a ctypes.Structure
? How do I have to modify the Vec3dS
class to give numpy a hint on how to convert it to a numpy array with the right dtype and values?
Edit: I suspect ctypes.Structure
implements PEP 3118 which takes precedence over __array__
. Is it possible to overwrite this from the python side?
Upvotes: 2
Views: 138
Reputation: 97631
If you use np.zeros(2, dtype=Vec3dS)
instead of np.zeros((2,3))
then you don't need to use __array__
at all on the ctypes structure:
from ctypes import Structure, c_double
import numpy as np
class Vec3d:
x = 1
y = 2
z = 3
def __array__(self, dtype):
return np.array(
(self.x, self.y, self.z),
dtype=[('x', np.double), ('y', np.double), ('z', np.double)])
class Vec3dS(Structure):
_fields_ = [("x", c_double), ("y", c_double), ("z", c_double)]
v = Vec3d()
vs = Vec3dS(1,2,3)
n = np.zeros(2, dtype=Vec3dS)
# note: since the array is 1d, we need `...` to prevent decay to scalars, which
# don't behave well with `__array__`
n[0, ...] = v
n[1, ...] = vs
Upvotes: 0
Reputation: 40733
Using numpy.frombuffer
seems to work well enough.
v = Vec3d(1,2,3)
l = [1,2,3]
n = np.zeros((2,3))
n[0] = l
n[1] = np.frombuffer(v)
assert (n[0] == n[1]).all()
Upvotes: 0