Best tuples questions in March 2012

In Python, why tuples, an immutable type, can contain mutable items such as lists?

50 votes

In Python, why tuples, an immutable type, can contain mutable items such as lists?

It is seemingly a contradiction that when a mutable item such as a list does get modified, the tuple it belongs to maintains being immutable.

That's an excellent question.

Tuples are characterized less by their immutability and more by their intended purpose. Tuples are Python's way of collecting heterogenous pieces of information under one roof. For example, s = ('www.python.org', 80) brings together a string and a number so that the host/port pair can be passed around as a socket, a composite object. Viewed in that light, it is perfectly reasonable to have mutable components.

Immutability goes hand-in-hand with another property, hashability. But hashability isn't an absolute property. If one of the tuple's components isn't hashable, then the overall tuple isn't hashable either. For example, t = ('red', [10, 20, 30]) isn't hashable.

The last example shows a 2-tuple that contains a string and a list. The tuple itself isn't mutable (i.e. it doesn't have any methods that for changing its contents). Likewise, the string is immutable because strings don't have any mutating methods. The list object does have mutating methods, so it can be changed. This shows that mutability is a property of an object type -- some objects have mutating methods and some don't. This doesn't change just because the objects are nested.

Remember, immutability is not magic. It is just an object that is read-only (ie. it doesn't have any update methods).

Hope, this was useful to you :-)

Convert rgb color to english color name, like 'green'

11 votes

I want to convert a color tuple to a color name, like 'yellow' or 'blue'

>>> im = Image.open("test.jpg")
>>> n, color = max(im.getcolors(im.size[0]*im.size[1]))
>>> print color
(119, 172, 152)

Is there a simple way in python to do this?

It looks like webcolors will allow you to do this:

rgb_to_name(rgb_triplet, spec='css3')

Convert a 3-tuple of integers, suitable for use in an rgb() color triplet, to its corresponding normalized color name, if any such name exists; valid values are html4, css2, css21 and css3, and the default is css3.

Example:

>>> rgb_to_name((0, 0, 0))
'black'

it is vice-versa-able:

>>> name_to_rgb('navy')
(0, 0, 128)

To find the closest colour name:

However webcolors raises an exception if it can't find a match for the requested colour. I've written a little fix that delivers the closest matching name for the requested RGB colour. It matches by Euclidian distance in the RGB space.

import webcolors

def closest_colour(requested_colour):
    min_colours = {}
    for key, name in webcolors.css3_hex_to_names.items():
        r_c, g_c, b_c = webcolors.hex_to_rgb(key)
        rd = (r_c - requested_colour[0]) ** 2
        gd = (g_c - requested_colour[1]) ** 2
        bd = (b_c - requested_colour[2]) ** 2
        min_colours[(rd + gd + bd)] = name
    return min_colours[min(min_colours.keys())]

def get_colour_name(requested_colour):
    try:
        closest_name = actual_name = webcolors.rgb_to_name(requested_colour)
    except ValueError:
        closest_name = closest_colour(requested_colour)
        actual_name = None
    return actual_name, closest_name

requested_colour = (119, 172, 152)
actual_name, closest_name = get_colour_name(requested_colour)

print "Actual colour name:", actual_name, ", closest colour name:", closest_name

Output:

Actual colour name: None , closest colour name: cadetblue