python-2.7 - 在 Python 2.7 和 Python 3.4 中带有内存 View 的 Ctypes from_buffer

标签 python-2.7 type-conversion ctypes python-3.4 memoryview

我正在尝试将数据从 memoryview 传递到 ctypes 数组,该数组在 Python 3.4 中运行良好,但在 Python 2.7 中却不行。

当我跑

from ctypes import c_byte
data = memoryview(b'012')
array = c_byte * 3
array.from_buffer_copy(data)

我得到 <__main__.c_byte_Array_3 at 0x7f3022cb8730>在 Python 3.4 中,但在 Python 2.7.6 中出现以下错误:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected a readable buffer object

这个错误的原因是什么,我如何在这两种情况下都能做到这一点?

我知道我可以使用将数据转换为字节
array.from_buffer_copy(data.tobytes())

但我认为这会产生一个额外的数据副本并且不优雅,因此我正在寻找更好的解决方案(也欢迎任何关于 tobytes 方法是否有效的评论)。

最佳答案

这是一个类,可让您使用 Python 2 memoryview 导出的缓冲区接口(interface)创建 ctypes 数组。对象。

from ctypes import *

pyapi = PyDLL("PythonAPI", handle=pythonapi._handle)

PyBUF_SIMPLE   = 0
PyBUF_WRITABLE = 0x0001
PyBUF_FORMAT   = 0x0004
PyBUF_ND       = 0x0008
PyBUF_STRIDES  = 0x0010 | PyBUF_ND

PyBUF_C_CONTIGUOUS   = 0x0020 | PyBUF_STRIDES
PyBUF_F_CONTIGUOUS   = 0x0040 | PyBUF_STRIDES
PyBUF_ANY_CONTIGUOUS = 0x0080 | PyBUF_STRIDES
PyBUF_INDIRECT       = 0x0100 | PyBUF_STRIDES

PyBUF_CONTIG_RO  = PyBUF_ND
PyBUF_CONTIG     = PyBUF_ND | PyBUF_WRITABLE

PyBUF_STRIDED_RO = PyBUF_STRIDES
PyBUF_STRIDED    = PyBUF_STRIDES | PyBUF_WRITABLE

PyBUF_RECORDS_RO = PyBUF_STRIDES | PyBUF_FORMAT
PyBUF_RECORDS    = PyBUF_STRIDES | PyBUF_FORMAT | PyBUF_WRITABLE

PyBUF_FULL_RO = PyBUF_INDIRECT | PyBUF_FORMAT
PyBUF_FULL    = PyBUF_INDIRECT | PyBUF_FORMAT | PyBUF_WRITABLE

Py_ssize_t = c_ssize_t
Py_ssize_t_p = POINTER(Py_ssize_t)

class pybuffer(Structure):
    """Python 3 Buffer Interface"""
    _fields_ = (('buf', c_void_p),
                ('obj', c_void_p), # owned reference
                ('len', Py_ssize_t),
                # itemsize is Py_ssize_t so it can be pointed to
                # by strides in the simple case.
                ('itemsize', Py_ssize_t),
                ('readonly', c_int),
                ('ndim', c_int),
                ('format', c_char_p),
                ('shape', Py_ssize_t_p),
                ('strides', Py_ssize_t_p),
                ('suboffsets', Py_ssize_t_p),
                # static store for shape and strides of
                # mono-dimensional buffers.
                ('smalltable', Py_ssize_t * 2),
                ('internal', c_void_p))

    def get_buffer(self, obj=None, flags=PyBUF_SIMPLE):
        self.release_buffer()
        Structure.__init__(self)
        if obj is not None:
            pyapi.PyObject_GetBuffer(obj, byref(self), flags)

    def make_release_buffer():
        import ctypes
        PyBuffer_Release = pyapi.PyBuffer_Release
        memset = ctypes.memset
        byref = ctypes.byref
        sizeof = ctypes.sizeof
        def release_buffer(self):
            if self.obj:
                PyBuffer_Release(byref(self))
                memset(byref(self), 0, sizeof(self))
        return release_buffer

    __init__ = get_buffer
    __del__ = release_buffer = make_release_buffer()
    del make_release_buffer        

    @property
    def as_ctypes(self):
        if self.obj and self.buf:
            arr = (c_char * self.len).from_address(self.buf)
            if self.readonly:
                arr = type(arr).from_buffer_copy(arr)
            else:
                obj = py_object.from_buffer(c_void_p(self.obj)).value
                arr._obj = obj
            return arr


pyapi.PyObject_GetBuffer.argtypes = (py_object,          # obj
                                     POINTER(pybuffer),  # view
                                     c_int)              # flags
pyapi.PyBuffer_Release.argtypes = POINTER(pybuffer),     # view

__all__ = [n for n in list(globals()) if n.startswith('PyBUF')]
__all__.append('pybuffer')

例子:

>>> data = memoryview(b'012')
>>> buf = pybuffer(data)
>>> buf.readonly
1
>>> array = buf.as_ctypes
>>> array[0] = '9'
>>> data[0]
'0'
>>> data = memoryview(bytearray(b'012'))
>>> buf = pybuffer(data)
>>> buf.readonly
0
>>> array = buf.as_ctypes
>>> array[0] = '9'
>>> data[0]
'9'

关于python-2.7 - 在 Python 2.7 和 Python 3.4 中带有内存 View 的 Ctypes from_buffer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28984692/

相关文章:

python - 如何从 csv 中读取字节作为字节?

c - Python调用C语言时,返回值异常

python - 尝试使用 ctypes 调用 wincred api

python - 我想要第 6 列值的总和

python - 如何使用元组打印此图案?

c++ - 存在 Windows 时未找到 Python.h

将整数转换为二进制

python - 我正在尝试运行一个程序,该程序会根据 raw_input 告诉您一个字母在字符串中重复了多少次

c++ - 如何检查 std::string 是否确实是整数?

python和ctypes cdll,没有从函数中获得预期的返回