我正在尝试将数据从 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/