Python - 将struct解压缩为多个元组

时间:2013-05-29 20:56:42

标签: python tuples iterable-unpacking

我想知道在Python 2.7中是否有更简洁的方法来执行以下操作?

# Current working code!
(is_enabled,) = struct.unpack_from("<?", data)
cmd_speed = struct.unpack_from("<3h", data, 1)
tach_speed = struct.unpack_from("<3h", data, 1+2*3)

具体来说,我不喜欢手动跟踪下一个元组的偏移量。 理想情况下,我希望能够使用单个格式语句指定数据结构;像这样:

# Hypothetical example, does not work! 
(is_enabled,), cmd_speed, tach_speed = struct.unpack("<(?),(3h),(3h)", data)

2 个答案:

答案 0 :(得分:7)

您可以通过拨打struct.unpack来完成此操作,但您仍需要自行切分结果:

import struct
data = struct.pack('<?3h3h', True, 1,2,3,4,5,6)
result = struct.unpack('<?3h3h', data)
is_enabled = result[0]
cmd_speed = result[1:4]
tach_speed = result[4:7]

print(is_enabled, cmd_speed, tach_speed)

产量

(True, (1, 2, 3), (4, 5, 6))

或者,你可以使用它:

import struct
import itertools as IT

def unpack_formats(fmts, data):
    data = iter(data)
    return [struct.unpack(fmt, ''.join(IT.islice(data, struct.calcsize(fmt))))
            for fmt in fmts]

data = struct.pack('<?3h3h', True, 1,2,3,4,5,6)
fmts = ('<?', '<3h', '<3h')
(is_enabled,), cmd_speed, tach_speed = unpack_formats(fmts, data)
print(is_enabled, cmd_speed, tach_speed)

产生

(True, (1, 2, 3), (4, 5, 6))

虽然unpack_formats看起来更漂亮,但实际上速度更快(可能因为不需要''.join):

def unpack_formats2(fmts, data):
    result = []
    i = 0
    for fmt in fmts:
        size = struct.calcsize(fmt)
        j = i+size
        result.append(struct.unpack(fmt, data[i:j]))
        i = j
    return result

In [80]: %timeit unpack_formats(fmts, data)
100000 loops, best of 3: 3.51 us per loop

In [81]: %timeit unpack_formats2(fmts, data)
1000000 loops, best of 3: 1.61 us per loop

答案 1 :(得分:1)

我通过使用带有偏移量的unpack_from而不是使用切片解压缩来调整@ unutbu的答案。

def unpack_formats3(fmts, data):
    result = []
    offset = 0
    for fmt in fmts:
        result.append(struct.unpack_from(fmt, data, offset))
        offset += struct.calcsize(fmt)
    return result

data = struct.pack('<?3h3h', True, 1,2,3,4,5,6)
fmts = ('<?', '<3h', '<3h')
(is_enabled,), cmd_speed, tach_speed = unpack_formats3(fmts, data)

print(is_enabled, cmd_speed, tach_speed)
(True, (1, 2, 3), (4, 5, 6))