# Generating random color codes in Python 3

Revision history
• 22 Jun 2019: Post was created (diff)
Tags: python

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.

• I was thinking of a 24-bit RGB color in hex representation, e.g. `#ff9900` (I come from HTML and CSS)
• But hex is just a representation/format, like octal and decimal. In decimal, a 24-bit color is a number between 0 and 16777216. (`16**6` which means `0-F` 6 times)
• The web colors consist of red, green and blue (RGB) channels. Maybe I should return a byte for each channel instead of all at once. Maybe it matters in terms of distribution?
• Can I do this with `str.format` or `%` formatting?

``````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, barr, barr)

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, barr, barr)

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
`````` 