python ctypes vs namedtuple

时间:2013-04-26 18:41:15

标签: python namedtuple

所以我有两个简单的ctypes struct

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操作会关注值类型问题,对吗?

1 个答案:

答案 0 :(得分: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个值,而不是ctypes个数组。如果你想要,你必须明确地投射它们。


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

s1.C = s2

最重要的是,namedtuple不能用作ctypes.Structure - 你不能将它传递给C函数,你必须编写手动逻辑(例如,{{1}左右如果你想用某种特定的二进制格式等序列化它,等等。


  

如何在namedtuple中处理列表?

如上所述,struct.pack的成员不是静态类型的,并且可以保存任何类型的值。因此,它们的处理方式与它们在namedtupletuple,普通类实例,全局变量等中的处理方式相同。只需将list放在那里即可得到了list


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

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

至于如何使用s2来序列化数据:struct模块没有任何方法可以直接委托给嵌入对象。但由于struct的输出只是pack(或者在Python 2.x,bytes)对象中,因此可以使用正常的字符串操作来执行此操作:

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

我认为第二个版本更容易阅读,但它也更容易出错,并且要求你考虑在二进制级别而不是Python级别编写对象,所以...选择你发现两个邪恶中较小的一个