4
\$\begingroup\$

My background is in C, and I'm finally learning this new-fangled "Python" that all the cool kids are talking about.

I want to create a list of formatted hexadecimal values like so:

['0x00','0x01','0x02','0x03' (...) '0xfe','0xff']

Please note that the leading zero's shouldn't be stripped. For example, I want 0x03, not 0x3.

My first successful attempt was this:

hexlist=list()
tempbytes = bytes(range(256))
for value in tempbytes:
    hexlist.append("0x" + tempbytes[value:value+1].hex())
del tempbytes

But, wow this is ugly. Then I tried to make it more Pythonic ("Pythony"?) like so:

hexlist = ["0x"+bytes(range(256))[x:x+1].hex() for x in bytes(range(256))]

My thoughts:

  1. OMG! That's harder to read, not easier!
  2. Not only that, but I had to invoke range twice, which I assume is inefficient.
  3. I bet there's a much better way that I haven't been exposed to yet...

My questions:

  1. Is the second much less efficient?
  2. Is there a better way to do this?
  3. What's the best thing to do here in keeping with python style?
\$\endgroup\$
3
\$\begingroup\$

The shortest and most readable way to do it would probably be

hexlist = [hex(x) for x in range(256)]

Time

Your first : 0.1 ms per loop for \$10^5\$ loops

Your second: 1 ms per loop for \$10^5\$ loops

The above : 0.02 ms per loop for \$10^5\$ loops


I'm unable to com up with a worse example, but there is always a worse way to do it, exactly as there is always preferable way to do it.

Edit To do this with filled zeros I would suggest to treat the first 10 numbers as a special case.

hexlist = [hex(x) if x > 15 else "{:#04x}".format(x) for x in range(256)]

new time: 0.04 ms. Even if it isn't as pretty.


Edit:

And when considering that format can format binary numbers, why not hex?

hexlist = ["{:#04x}".format(x) for x in range(256)]

I guess that this is hard coded for this range, but anyway.

Time: 0.1ms for per loop for \$10^5\$ loops.


And more edits

Using old style:.

hexlist = ["0x%02x" % n for n in range(256)]

Time: 0.08ms per loop for \$10^5\$ loops.


And specifically generating what we want.

hexlist = ["{:#04x}".format(x) for x in range(16)] + \
              [hex(x) for x in range(16, 256)]

Time: 0.03ms per loop for \$10^5\$ loops.

\$\endgroup\$
  • \$\begingroup\$ Excellent, Simon, thank you. Unfortunately, this strips off the leading zeros from the first 10 values. I wasn't clear in my question: I would like 0x03, for example, instead of 0x3. I've updated the question... \$\endgroup\$ – bitsmack Nov 16 '16 at 6:28
  • \$\begingroup\$ Maybe not as pretty as your first method, but a whole lot prettier than either of mine! :-D Thanks! \$\endgroup\$ – bitsmack Nov 16 '16 at 6:58
  • \$\begingroup\$ Glad to help, it was an very interesting question! \$\endgroup\$ – Simon Nov 16 '16 at 7:50
  • \$\begingroup\$ For roughly 0.13 ms per loop (over 10^5 loops), using "old-style" formatting strings, ["0x%02x" % n for in in range(256)] is short, readable and (probably) fast enough for once-off generation. \$\endgroup\$ – Vatine Nov 16 '16 at 10:17
  • 2
    \$\begingroup\$ Both your edits are using the Format Specification Mini-Language, format(x, "#04x") is the same as "{:#04x}".format(x). Also either way you could change them to '0x{:0>{}x}'.format(5, 2), or "{:#0{}x}".format(5, 4) if you don't want a static length. \$\endgroup\$ – Peilonrayz Nov 16 '16 at 10:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.