所以我有两个简单的 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) ]
Run Code Online (Sandbox Code Playgroud)
例如,是否可以使用namedtuple 执行相同的操作?nametuple 中如何处理列表?
编辑:
结构包用法
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])))
Run Code Online (Sandbox Code Playgroud)
最后我想以可读格式打印数据(具有可见的键:值对)。但现在我不知道应该如何处理两个不同的命名元组的解包操作......?
这个 unpack.struct 操作会处理值类型问题,对吗?
\n\n\n例如,是否可以使用namedtuple 执行相同的操作?
\n
这取决于你所说的“相同”是什么意思。您可以轻松创建namedtuple具有相同字段的类型:
S2 = collections.namedtuple(\'S2\', [\'A2\', \'B2\', \'C2\'])\nS1 = collections.namedtuple(\'S1\', [\'A\', \'B\', \'C\'])\nRun Code Online (Sandbox Code Playgroud)\n\n然而,它们显然不是同一类型,并且不会有相同的行为。
\n\n首先,这些字段是普通的 Python 属性(也是普通的tuple成员),这意味着它们没有静态类型;它们可以保存任何类型的值。
所以,你可以这样做:
\n\ns2 = S2([ctypes.c_uint16(i) for i in range(10)],\n [ctypes.c_uint32(i) for i in range(10)],\n [ctypes.c_uint32(i) for i in range(10)])\ns1 = S1(ctypes.c_uint16(1), ctypes.c_uint32(2), s2)\nRun Code Online (Sandbox Code Playgroud)\n\n但你也可以这样做:
\n\ns2 = S2(\'a\', \'b\', \'c\')\ns1 = S1(\'d\', \'e\', s2)\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6 甚至:
\n\ns1 = S1(\'d\', \'e\', \'f\')\nRun Code Online (Sandbox Code Playgroud)\n\n另外,请注意,即使第一个示例实际上也创建了list10 个ctypes值,而不是ctypes数组。如果你想要这样,你必须明确地投射它们。
其次,namedtuples 是 s 的扩展tuple,这意味着它们是不可变的,所以你不能这样做:
s1.C = s2\nRun Code Online (Sandbox Code Playgroud)\n\n最重要的是, anamedtuple不能用作ctypes.Structure\xe2\x80\x94 你不能将它传递给 C 函数,struct.pack如果你想以某种特定的方式序列化它,你必须编写手动逻辑(例如, around )二进制格式等
\n\n\nnametuple 中如何处理列表?
\n
如上所述, a 的成员namedtuple不是静态类型的,并且可以保存任何类型的值。tuple因此,它们的处理方式与 a 、list、普通类实例、全局变量等中的处理方式相同。只需将 alist放在那里,您就得到了list.
\n\n\n是的, Stuct.pack 正是我所需要的。但我不知道如何指出 s1 中的最后一个值是对 s2 结构的引用。
\n
从namedtuple侧面来看,您只需使用一个S2实例作为 的值S1.C,如我上面的示例所示。同样,a 的项目/属性namedtuple就像任何其他属性/变量/等一样。在 Python 中,只是保存对象引用的名称。因此,s1 = S1(1, 2, s2)将使第三项成为对所引用的s1同一对象的另一个引用。s2
至于如何使用struct序列化数据:该struct模块没有任何方法直接委托给嵌入对象。但由于 的输出pack只是一个bytes(或者,在 Python 2.x 中,str)对象,因此您可以使用普通的字符串操作来完成此操作:
# version 1\ns2_struct = struct.Struct(\'!HII\')\ns1_header = struct.Struct(\'!HI\')\ndef pack_s2(s2):\n return s2_struct.pack(s2.A2, s2.B2, s2.C2)\ndef unpack_s2(s2):\n return S2._make(s2_struct.unpack(s2))\ndef pack_s1(s1):\n return s1_header.pack(s1.A, s1.B) + pack_s2(s1.C)\ndef unpack_S1(s1):\n offset = len(s1_header)\n a, b = s1_header.unpack(s1[:offset])\n c = unpack_s2(s1[offset:])\n return S1._make(a, b, c)\nRun Code Online (Sandbox Code Playgroud)\n\n(我个人会使用S2(*struct.unpack而不是S2._make,但由于文档重复执行后者,我想这必须是执行操作的预期方式\xe2\x80\xa6)
或者,您可以手动展平格式字符串:
\n\ns2_struct = struct.Struct(\'!HII\')\ns1_struct = struct.Struct(\'!HIHII\')\ndef pack_s2(s2):\n return s2_struct.pack(s2.A2, s2.B2, s2.C2)\ndef pack_s1(s1):\n return s1_struct.pack(s1.A, s1.B, s1.C.A2, s1.C.B2, s1.C.C2)\ndef unpack_s2(s2):\n return S2._make(s2_struct.unpack(s2))\ndef unpack_S1(s1):\n a, b, a2, b2, c2 = s1_struct.unpack(s1)\n c = S2(a2, b2, c2)\n return S1(a, b, c)\nRun Code Online (Sandbox Code Playgroud)\n\n我认为第二个版本更容易阅读,但也更容易出错,并且需要你考虑在二进制级别而不是Python级别上组合对象,所以\xe2\x80\xa6选择你找到的那个两害相权取其轻。
\n