首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ctypes可变长度结构

ctypes可变长度结构
EN

Stack Overflow用户
提问于 2011-08-11 01:55:29
回答 7查看 9.4K关注 0票数 14

自从我读到Dave Beazley关于二进制I/O处理(http://dabeaz.blogspot.com/2009/08/python-binary-io-handling.html)的文章后,我就一直想为某种线路协议创建一个Python库。然而,我找不到可变长度结构的最佳解决方案。这是我想要做的:

代码语言:javascript
运行
复制
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解决方案是什么?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2011-08-11 02:46:56

对于您给出的示例,最直接的方法是在您拥有所需的信息时定义结构。

一种简单的方法是在你将要使用的地方创建类,而不是在模块根上-例如,你可以只将class主体放在一个函数中,它将充当一个工厂-我认为这是最具可读性的方式。

代码语言:javascript
运行
复制
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以另一种方式发布。

票数 13
EN

Stack Overflow用户

发布于 2011-08-11 04:14:39

现在,对于一些完全不同的东西-如果您所需要的只是处理数据,那么“最Pythonic”的方法可能就是根本不尝试使用ctype来处理内存中的原始数据。

这种方法只是使用struct.pack和.unpack来序列化/反序列化数据,因为它在你的应用上移动/离开你的应用。"Points“类可以接受原始字节,并从中创建python对象,并且可以通过"get_data”方法序列化数据。否则,它只是一个普通的python列表。

代码语言:javascript
运行
复制
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)
票数 3
EN

Stack Overflow用户

发布于 2017-03-17 03:52:27

这个问题真的,真的,老了:

我有一个更简单的答案,这看起来很奇怪,但避免了元类,并解决了ctype不允许我直接构建具有与C相同定义的结构的问题。

来自内核的示例C结构:

代码语言:javascript
运行
复制
struct some_struct {
        __u32   static;
        __u64   another_static;
        __u32   len;
        __u8    data[0];
};

使用ctype实现:

代码语言:javascript
运行
复制
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

使用示例输出:

代码语言:javascript
运行
复制
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>

这个答案对我来说很有效,原因有两个:

  1. 构造函数的参数可以进行酸洗,并且没有对类型的引用。
  2. 我定义了StructwithVariableArrayLength定义内部的所有结构。
  3. 对于调用者来说,结构看起来就像我刚刚在_fields_
  4. 内部定义了数组一样。我无法修改头文件中定义的底层结构,也无法在不更改任何底层代码的情况下实现我的目标。
  5. 我不必修改任何解析/打包逻辑,这只做我想做的事情,即用可变长度数组构建一个类定义。
  6. 这是一个通用的、可重用的容器,可以像我的其他结构一样发送到工厂中。

我显然更喜欢头文件接受一个指针,但这并不总是可能的。这个答案令人沮丧。其他的非常适合数据结构本身,或者需要修改调用者。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7015487

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档