1

所以我有两个简单的 ctypes 结构

class S2 (ctypes.Structure):
    _fields_ = [
    ('A2',     ctypes.c_uint16*10),
    ('B2',     ctypes.c_uint32*10),
    ('C2',     ctypes.c_uint32*10) ]


class S1 (ctypes.Structure):
    _fields_ = [
    ('A',     ctypes.c_uint16),
    ('B',     ctypes.c_uint32),
    ('C',     S2) ]

例如,是否可以使用 namedtuple 做同样的事情?在 namedtuple 中如何处理列表?

编辑:

struc.pack 用法

test_data = '0100000002000000' + 10*'01' + 10*'01' + 10*'01'

S2 = collections.namedtuple('S2', ['A2', 'B2', 'C2'])
S1 = collections.namedtuple('S1', ['A', 'B', 'REF_to_S2'])

Data2 = S2._make(struct.unpack('10p10p10p', binascii.unhexlify(test_data[16:])))
##this is not working, because there must be 3 args..
Data1 = S1._make(struct.unpack('ii', binascii.unhexlify(test_data[0:16])))

最后,我想以可读格式打印数据(键:值对可见)。但是现在我无法弄清楚我应该如何使用两个不同的命名元组来处理解包操作......?

这个 unpack.struct 操作会处理值类型问题,对吧?

4

1 回答 1

4

例如,是否可以使用 namedtuple 做同样的事情?

这取决于您所说的“相同”是什么意思。您可以轻松地创建namedtuple具有相同字段的类型:

S2 = collections.namedtuple('S2', ['A2', 'B2', 'C2'])
S1 = collections.namedtuple('S1', ['A', 'B', 'C'])

但是,它们显然不是同一类型,并且不会具有相同的行为。


首先,这些字段是普通的 Python 属性(也是普通的tuple成员),这意味着它们没有静态类型;它们可以保存任何类型的值。

所以,你可以这样做:

s2 = S2([ctypes.c_uint16(i) for i in range(10)],
        [ctypes.c_uint32(i) for i in range(10)],
        [ctypes.c_uint32(i) for i in range(10)])
s1 = S1(ctypes.c_uint16(1), ctypes.c_uint32(2), s2)

但你也可以这样做:

s2 = S2('a', 'b', 'c')
s1 = S1('d', 'e', s2)

… 甚至:

s1 = S1('d', 'e', 'f')

另外,请注意,即使是第一个示例实际上也创建list了 10 个ctypes值的 s,而不是ctypes数组。如果你想要这样,你必须明确地转换它们。


其次,namedtuples 是 s 的扩展tuple,这意味着它们是不可变的,所以你不能这样做:

s1.C = s2

最重要的是, anamedtuple不能用作 a ctypes.Structure- 你不能将它传递给 C 函数,struct.pack如果你想以某种特定的二进制格式序列化它,你必须编写手动逻辑(例如, around )等。


在 namedtuple 中如何处理列表?

如上所述, a 的成员namedtuple不是静态类型的,并且可以保存任何类型的值。因此,它们的处理方式与它们在tuple, list、普通类实例、全局变量等中的处理方式相同。只需将 a 粘贴list在那里,你就有了list.


是的,stuct.pack 就是我需要的。但我无法弄清楚我应该如何指出 s1 中的最后一个值是对 s2 结构的引用。

namedtuple侧面看,您只需使用一个S2实例作为 的值S1.C,如我上面的示例所示。同样, a 的项目/属性namedtuple就像任何其他属性/变量/等一样。在 Python 中,只是包含对对象的引用的名称。所以,s1 = S1(1, 2, s2)会使得第三项的s1引用变成另一个引用的同一个对象s2

至于如何使用struct序列化数据:该struct模块没有任何方法可以直接委托给嵌入式对象。但由于 的输出pack只是一个bytes(或者,在 Python 2.x 中,str)对象,您可以通过正常的字符串操作来做到这一点:

# version 1
s2_struct = struct.Struct('!HII')
s1_header = struct.Struct('!HI')
def pack_s2(s2):
    return s2_struct.pack(s2.A2, s2.B2, s2.C2)
def unpack_s2(s2):
    return S2._make(s2_struct.unpack(s2))
def pack_s1(s1):
    return s1_header.pack(s1.A, s1.B) + pack_s2(s1.C)
def unpack_S1(s1):
    offset = len(s1_header)
    a, b = s1_header.unpack(s1[:offset])
    c = unpack_s2(s1[offset:])
    return S1._make(a, b, c)

(我个人会使用S2(*struct.unpack而不是S2._make,但由于文档反复使用后者,我想这必须是做事的预期方式......)

或者,您可以手动展平格式字符串:

s2_struct = struct.Struct('!HII')
s1_struct = struct.Struct('!HIHII')
def pack_s2(s2):
    return s2_struct.pack(s2.A2, s2.B2, s2.C2)
def pack_s1(s1):
    return s1_struct.pack(s1.A, s1.B, s1.C.A2, s1.C.B2, s1.C.C2)
def unpack_s2(s2):
    return S2._make(s2_struct.unpack(s2))
def unpack_S1(s1):
    a, b, a2, b2, c2 = s1_struct.unpack(s1)
    c = S2(a2, b2, c2)
    return S1(a, b, c)

我认为第二个版本更容易阅读,但也更容易出错,并且需要您考虑在二进制级别而不是 Python 级别组合对象,所以……选择您认为两害相权取其轻的那个。

于 2013-04-26T19:16:39.907 回答