例如,是否可以使用 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
数组。如果你想要这样,你必须明确地转换它们。
其次,namedtuple
s 是 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 级别组合对象,所以……选择您认为两害相权取其轻的那个。