简体   繁体   中英

Python/Pygame colours are no tuples: unhashable type error when used as dictionary keys

Rough background:

I wrote a few functions in Python/Pygame to create a tiled level map from a coloured mini-map. One pixel on the small one stands for a certain type of tile on the big one.

Now I found out, that the colours you get by using Pygame's surface.get_at((x, y)) are no tuples despite the fact that they look like that...

Short example of the result of get_at on a black surface:

surf = pygame.Surface((64, 64)).convert()
surf.fill((0, 0, 0))

print(surf.get_at((32, 32))

Print:

(0, 0, 0, 255)

So I thought, creating a dictionary with the colours as key would work just like that:

tiledict = {(0, 0, 0, 255): [tile list 1], (255, 0, 0, 255): [tile list 2], etc}

for y in range(0, tilemap.get_height()):
    for x in range(0, tilemap.get_width()):
        clr = tilemap.get_at((x, y))

        lvlmap.blit(tiledict[clr][random.randint(0, len(tiledict[clr]) - 1)]

Result:

    lvlmap.blit(tiledict[clr][random.randint(0, len(tiledict[clr]) - 1)]
TypeError: unhashable type: 'pygame.Color'

Yet when I define the colours as tuples before using them as keys, it works:

GRAY = (128, 128, 128, 255)

print(GRAY == clr)

yields:

True

I have my workaround: I made the dictionary keys all strings and then do the get_at call this way

clr = str(tilemap.get_at((x, y)))

Does anybody have a clue what the issue is here? I find it rather interesting. Colours are no tuples and therefore not hashable, but when I ask, whether it's equal to a tuple, the return is "True"...

I also tried to define all colours as tuples and it worked as well. But that approach is way slower than my current one.

Does anybody have a clue of what else I could try without having to chance types?

The fact that the equality operator works for a tuple and a pygame.Color does NOT mean that you can index into a dict with a pygame.Color. You're thinking that's the same comparison but dicts need key objects to be hashable.

The PyGame.Color type specifically supports comparisons to tuples , provided the tuple is of length 3 or 4 and contains integers in the range 0-255 (see the RGBAFromObj source ).

This is explicitly documented on the PyGame.Color page :

Color objects support equality comparison with other color objects and 3 or 4 element tuples of integers

Any Python class can implement custom equality tests by implementing a __eq__ method .

The type also implements everything to be a sequence, returning the R, G, B and A components; you can turn a PyGame.Color into a tuple by calling tuple() on it:

tiledict[tuple(clr)]

and turn it back into a PyGame.Color object by simply passing in the tuple as separate arguments:

pygame.Color(*tuple_of_rgba_ints)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM