Search code examples
pythonpython-dataclasses

What is the Pythonic way of using a moving x,y coordinate dataclass as a dict key?


from dataclasses import dataclass

@dataclass
class coordinate:
    x: int
    y: int

objects = {}

pos = coordinate(0, 0)

objects[pos] = "A"

pos.x += 1 # Changing the current position I am looking at

objects[pos] = "B"

pos.y += 1

objects[pos] = "C"

for position in objects:
    print(position, objects[position])


This throws TypeError: unhashable type: 'coordinate'.

Setting @dataclass(frozen=True, eq=True) throws dataclasses.FrozenInstanceError: cannot assign to field 'x'.

And finally, using @dataclass(unsafe_hash=True) results in:

coordinate(x=1, y=1) C
coordinate(x=1, y=1) C
coordinate(x=1, y=1) C

One way way to do this is to use objects[(pos.x, pos.y)], but that just seems to defeat the purpose of using the dataclass in the first place. Is there a better, more pythonic way of doing this?


Solution

  • dict keys should be immutable objects, so frozen=True is non-negotiable.

    To "modify" a frozen object, you need to make a copy of it. The dataclasses module provides a convenience function for that, replace.

    from dataclasses import dataclass, replace
    
    @dataclass(frozen=True)
    class coordinate:
        x: int
        y: int
    
    objects = {}
    
    pos = coordinate(0, 0)
    
    objects[pos] = "A"
    
    pos = replace(pos, x=pos.x+1) # Changing the current position I am looking at
    
    objects[pos] = "B"
    
    pos = replace(pos, y=pos.y+1) 
    
    objects[pos] = "C"
    
    for position in objects:
        print(position, objects[position])