Generating random color codes in Python 3

I was looking at question from Stack Overflow asking How to use random to choose colors. I wanted to take a shot at it before reading the answers again.

At first I made a function to return a hexadecimal string

import random

def rand_web_color_hex():
    rgb = ""
    for _ in "RGB":
        i = random.randrange(0, 2**8)
        rgb += i.to_bytes(1, "big").hex()
    return rgb

print(rand_web_color_hex()) # 2f04d8
print(rand_web_color_hex()) # 8dbc53
print(rand_web_color_hex()) # 022632

Then I thought I might make a function to return a decimal number, too

import random

def rand_web_color_dec():
    return random.randrange(0, 16**6)

print(rand_web_color_dec()) # 7431420
print(rand_web_color_dec()) # 12678862
print(rand_web_color_dec()) # 582912

Naturally, a lot smaller. A simple call to randrange() to return a 24-bit integer. Makes me want to revisit the hex function to simplify it. Hex is simply a number formatted in base16, and for web colors, they are zero-padded to maintain their length of 6 hexadecimal chars. I know there’s a way to return numbers in hex notation using str.format or % formatting.

def rand_web_color_hex():
    return "%06x" % random.randrange(0, 16**6)

print(rand_web_color_hex()) # 06dffd
print(rand_web_color_hex()) # 6f0e2a
print(rand_web_color_hex()) # 1d3df2

In the above code, I’m using the % string formatting operator from PEP 3101, where %x will format a number user lower-case hex notation, 6 pads the string with spaces to make it consist of at least six characters, and lastly, the 0 will use zeros for padding instead of spaces.

Now, if I want to return a color with separated channels, like rgb(r, g, b) format, I must add another function. I can use bytearray.fromhex() to convert from hex to a byte array.

def rand_web_color_hex():
    return "%06x" % random.randrange(0, 16**6)

def to_rgb(color_str):
    barr = bytearray.fromhex(color_str)
    return (barr[0], barr[1], barr[2])

hex_color = rand_web_color_hex()
print(hex_color, to_rgb(hex_color)) # 958c3b (149, 140, 59)

Cleaning up and refactoring a bit, yields the following code.

import random

def rand_24_bit():
    """Returns a random 24-bit integer"""
    return random.randrange(0, 16**6)

def color_dec():
    """Alias of rand_24 bit()"""
    return rand_24_bit()

def color_hex(num=rand_24_bit()):
    """Returns a 24-bit int in hex"""
    return "%06x" % num

def color_rgb(num=rand_24_bit()):
    """Returns three 8-bit numbers, one for each channel in RGB"""
    hx = color_hex(num)
    barr = bytearray.fromhex(hx)
    return (barr[0], barr[1], barr[2])

c = color_dec()
print("%8d #%6s rgb%s" % ( c, color_hex(c), color_rgb(c) ))

This was a good exercise for maths and formatting in Python. Hope you liked it too!

Side notes

For the formatting of the decimal notation, I wanted to figure out the maximum number of digits a 24-bit number could consist of in base10. @capitol of Hackeriet helped me out with the formula. Thanks!

ceil( 24 * (log(2) / log(10) ) = 8

References

If you have any comments or feedback, please send me an e-mail. (stig at stigok dotcom)