简体   繁体   中英

Convert ctypes object to numpy array

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?

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()

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM