自从我读到Dave Beazley关于二进制I/O处理(http://dabeaz.blogspot.com/2009/08/python-binary-io-handling.html)的文章后,我就一直想为某种线路协议创建一个Python库。然而,我找不到可变长度结构的最佳解决方案。这是我想要做的:
import ctypes as c
class Point(c.Structure):
_fields_ = [
('x',c.c_double),
('y',c.c_double),
('z',c.c_double)
]
class Points(c.Structure):
_fields_ = [
('num_points', c.c_uint32),
('points', Point*num_points) # num_points not yet defined!
]
因为还没有定义num_points
,所以类Points
将不起作用。一旦知道了num_points
,我可以稍后重新定义_fields_
变量,但是因为它是一个类变量,所以它会影响所有其他Points
实例。
这个问题的pythonic解决方案是什么?
发布于 2011-08-11 02:46:56
对于您给出的示例,最直接的方法是在您拥有所需的信息时定义结构。
一种简单的方法是在你将要使用的地方创建类,而不是在模块根上-例如,你可以只将class
主体放在一个函数中,它将充当一个工厂-我认为这是最具可读性的方式。
import ctypes as c
class Point(c.Structure):
_fields_ = [
('x',c.c_double),
('y',c.c_double),
('z',c.c_double)
]
def points_factory(num_points):
class Points(c.Structure):
_fields_ = [
('num_points', c.c_uint32),
('points', Point*num_points)
]
return Points
#and when you need it in the code:
Points = points_factory(5)
对不起--C代码会帮你“填充”这些值--这不是答案。WIll以另一种方式发布。
发布于 2011-08-11 04:14:39
现在,对于一些完全不同的东西-如果您所需要的只是处理数据,那么“最Pythonic”的方法可能就是根本不尝试使用ctype来处理内存中的原始数据。
这种方法只是使用struct.pack和.unpack来序列化/反序列化数据,因为它在你的应用上移动/离开你的应用。"Points“类可以接受原始字节,并从中创建python对象,并且可以通过"get_data”方法序列化数据。否则,它只是一个普通的python列表。
import struct
class Point(object):
def __init__(self, x=0.0, y=0.0, z= 0.0):
self.x, self.y, self.z = x,y,z
def get_data(self):
return struct.pack("ddd", self.x, self.y, self.z)
class Points(list):
def __init__(self, data=None):
if data is None:
return
pointsize = struct.calcsize("ddd")
for index in xrange(struct.calcsize("i"), len(data) - struct.calcsize("i"), pointsize):
point_data = struct.unpack("ddd", data[index: index + pointsize])
self.append(Point(*point_data))
def get_data(self):
return struct.pack("i", len(self)) + "".join(p.get_data() for p in self)
发布于 2017-03-17 03:52:27
这个问题真的,真的,老了:
我有一个更简单的答案,这看起来很奇怪,但避免了元类,并解决了ctype不允许我直接构建具有与C相同定义的结构的问题。
来自内核的示例C结构:
struct some_struct {
__u32 static;
__u64 another_static;
__u32 len;
__u8 data[0];
};
使用ctype实现:
import ctypes
import copy
class StructureVariableSized(ctypes.Structure):
_variable_sized_ = []
def __new__(self, variable_sized=(), **kwargs):
def name_builder(name, variable_sized):
for variable_sized_field_name, variable_size in variable_sized:
name += variable_sized_field_name.title() + '[{0}]'.format(variable_size)
return name
local_fields = copy.deepcopy(self._fields_)
for matching_field_name, matching_type in self._variable_sized_:
match_type = None
for variable_sized_field_name, variable_size in variable_sized:
if variable_sized_field_name == matching_field_name:
match_type = matching_type
break
if match_type is None:
raise Exception
local_fields.append((variable_sized_field_name, match_type*variable_size))
name = name_builder(self.__name__, variable_sized)
class BaseCtypesStruct(ctypes.Structure):
_fields_ = local_fields
_variable_sized_ = self._variable_sized_
classdef = BaseCtypesStruct
classdef.__name__ = name
return BaseCtypesStruct(**kwargs)
class StructwithVariableArrayLength(StructureVariableSized):
_fields_ = [
('static', ctypes.c_uint32),
('another_static', ctypes.c_uint64),
('len', ctypes.c_uint32),
]
_variable_sized_ = [
('data', ctypes.c_uint8)
]
struct_map = {
1: StructwithVariableArrayLength
}
sval32 = struct_map[1](variable_sized=(('data', 32),),)
print sval32
print sval32.data
sval128 = struct_map[1](variable_sized=(('data', 128),),)
print sval128
print sval128.data
使用示例输出:
machine:~ user$ python svs.py
<__main__.StructwithVariableArrayLengthData[32] object at 0x10dae07a0>
<__main__.c_ubyte_Array_32 object at 0x10dae0830>
<__main__.StructwithVariableArrayLengthData[128] object at 0x10dae0830>
<__main__.c_ubyte_Array_128 object at 0x10dae08c0>
这个答案对我来说很有效,原因有两个:
我显然更喜欢头文件接受一个指针,但这并不总是可能的。这个答案令人沮丧。其他的非常适合数据结构本身,或者需要修改调用者。
https://stackoverflow.com/questions/7015487
复制相似问题