Search code examples
pythonpython-3.xdictionarytypeerrormypy

Override __hash__ in python dict mypy error


I am writing a class that represents a hashable dictionary in Python, following what has been suggested in here: hashable dict. I am doing this because I need it to be hashable for other implementations.

So I basically created my nice HashableDict class:

class HashableBinnerDict(dict[KEY, VALUE]):
    """
    Class that represents a hashable dict.
    """

    def __hash__(self) -> int:
        return hash(frozenset(self))

It inherits from dict and KEY and VALUE are two generic datatypes needed to parametrize the typing of my HashableDict.

The overall code that leverages such class works perfectly. However, mypy is complaining with this error:

error: Signature of "__hash__" incompatible with supertype "dict"  [override]

And I guess it is caused by the fact that inside the "base" class dict of python there's not implemented hash function, in fact we have (extracted from the dict class in the Python codebase):

class dict(object):
    ...
    ... # skipping non relevant code

    def __sizeof__(self): # real signature unknown; restored from __doc__
        """ D.__sizeof__() -> size of D in memory, in bytes """
        pass

    __hash__ = None

The hash attribute is defined as None, so I guess mypy is complaining because of that. Any idea how to solve this besides brutally ignoring the error?


Solution

  • In the end I found a better solution to my problem (thanks to my colleagues for the suggestion :D).

    Implementing an hashable frozen dictionary is not a good idea, it is quite against the "core" of python.

    There are some workarounds with MappingProxyType that allows you to generate a read only dictionary. However, MappingProxyType is not hashable and it is not possible to override / create the __hash__ method because such class is marked as final.

    Overriding by hand all the possible methods of base python classes is quite tricky and really prone to errors: it is enough to forget to override one method that changes the dictionary and you're done.

    I found a workaround to ensure the hashability and immutability of my attribute while preserving the "usability" of a dictionary.

    I created my attribute as tuple[tuple[str, MyObject]] and then I have a cached_property that converts such tuple in a dictionary that I can use.

    So long story short: try to avoid weird overrides on python types to force a "non-intended" behaviour, change approach and be compliant with the python "philosophy"