6

I'd like to know if there is a cleaner way to do the following in 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)

Specifically, I don't like manually keeping track of the offset into the next tuple. Ideally I'd like to be able to specify the data structure with a single format statement; Something like this:

# Hypothetical example, does not work! 
(is_enabled,), cmd_speed, tach_speed = struct.unpack("<(?),(3h),(3h)", data)
Zero Piraeus
59.7k28 gold badges158 silver badges164 bronze badges
asked May 29, 2013 at 20:56

2 Answers 2

9

You could do it with one call to struct.unpack, but you'd still have to slice up the result yourself:

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)

yields

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

Or, you could use this:

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)

which yields

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

Although unpack_formats looks prettier, the following is actually faster (probably because there is no ''.join needed):

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
answered May 29, 2013 at 21:04
Sign up to request clarification or add additional context in comments.

1 Comment

Nice move with iter(data)
1

I tweaked @unutbu's answer a bit by using unpack_from with an offset instead of unpack with slices.

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))
answered May 30, 2013 at 0:17

1 Comment

Runs a bit slower on IPython, but faster on standard python 2.7 for me.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.