pythonstructunpack

Unpack tuple to mixed array, variables


I have string of bytes that come from embeded system and I use python struct.unpack() to give me tuple of values.

As there are a lot of values I wish to make code more readable. For now I have it done like this:

ArrA = [None] * 3
ArrB = [None] * 2

ArrA[0],ArrA[1],ArrA[2], n, ArrB[0],ArrB[1], crc = (1, 2, 3, 4, 5, 6, 7)
# tuple came from unpack()

Result:

ArrA = [1, 2, 3]
n = 4
ArrB = [5, 6]
crc = 7

Is there some way to shorten arrays? eg ArrA[0],ArrA[1],ArrA[2] to something like ArrA[]*3??

In example arrays are short in real they are pretty long.


Solution

  • I don't think there's a way to do this with unpacking. If you had only one list, you could use a starred expression*, but not with multiple lists.

    Here are some alternatives:

    Basic indexing and slicing

    t = (1, 2, 3, 4, 5, 6, 7)
    
    ArrA = list(t[:3])
    n = t[3]
    ArrB = list(t[4:6])
    crc = t[6]
    

    One difference is that you won't get an error if t is too long, but you can simply add a check, e.g:

    assert len(t) == 7, f"Wrong size: {len(t)}"
    

    Iterator

    If you'd rather focus on the number of elements in the lists rather than their positions in t, you can make t into an iterator and consume it a chunk at a time:

    it = iter(t)
    
    ArrA = [next(it) for _ in range(3)]
    n = next(it)
    ArrB = [next(it) for _ in range(2)]
    crc = next(it)
    

    This lets you check that t is not too long using this weird syntax:

    () = it  # Confirm exhausted
    

    Instead of using list comprehensions, islice is cleaner:

    from itertools import islice
    
    ArrA = list(islice(it, 3))
    ...
    ArrB = list(islice(it, 2))
    ...
    

    Pop from a stack (list)

    Python lists are like stacks, with pops happening from the end by default. So we just need to convert t to a list and reverse it to be able to use it similarly to the iterator solution above. It's also possible to pop from the start, but it's less efficient at large scales and I think this is cleaner anyway.

    L = list(t)[::-1]
    
    ArrA = [L.pop() for _ in range(3)]
    n = L.pop()
    ArrB = [L.pop() for _ in range(2)]
    crc = L.pop()
    
    () = L  # Confirm exhausted
    

    Footnotes

    * For example:

    t = (1, 2, 3, 4, 5, 6, 7)
    
    m, *ArrC, crd = t
    

    Result:

    m = 1
    ArrC = [2, 3, 4, 5, 6]
    crd = 7