Decode hex pairwise with zip

The other day I had to decode strings containing hex numbers into plain integers.

I could use built-in int([ x [, radix]]) with radix=16 to convert a string to an integer and forget about it, but I needed to decode pairs from the hex number so that in the end from the string ‘FFEEDD’ I get 255, 238, 221

But wouldn’t you know it, there is a standard solution to get pairs in Python and it’s built-in zip function.

It’s a well known function for experienced developers, but there is one documented feature of it that doesn’t always catch one’s eye and it’s a general form for clustering a data series into n-length groups zip(*[iter(s)]*n) which is described in official docs for zip built-in.

With that in mind solution to my problem boils down to a one-line decoder:

>>> s = 'FFDDEE'
>>> [int(''.join(pair), 16) for pair in zip(*[iter(s)]*2)]
[255, 221, 238]

For those wondering how it works:

1) iter(s) creates iterable from the s

>>> iter(s)
<iterator object at 0x8a3c2cc>

2) [iter(s)] * 2 creates list of 2 iterators that all use the same underlying iterable iter(s). The important thing is that created iterators share the same iterable, so when some data is consumed by first iterator that data becomes unavailable for the second iterator and vice versa.

You can see from the output that iterator objects are located at the same address. If you are new to this behavior you can read on about shallow copies.

>>> [iter(s)]*2
[<iterator object at 0x8a3c2cc>, <iterator object at 0x8a3c2cc>]

3) leading * in zip’s parameter unpacks list from step (2) into two separate parameters for zip function. Basically it becomes zip(iterator, iterator).

More about unpacking argument list you can read in official docs.

>>> zip(*[iter(s)]*2)
[('F', 'F'), ('D', 'D'), ('E', 'E')]

Leave a Reply