LRU cache unhashable type if subclass from Python dictionary

Viewed 747

I am trying to figure out why the following code does not work

from functools import lru_cache
import numpy as np

class Mask(dict):
    def __init__(self, shape, data = None):
        super().__init__(data)
        self.shape = shape

    @lru_cache(maxsize=1)
    def tomatrix(self):
        dense = np.zeros(self.shape[0] * self.shape[1])
        for uid, entries in self.items():
            dense[entries] = uid
        return np.reshape(dense, self.shape)

cells = {i : np.arange(i * 10, (i + 1) * 10) for i in range(10)}
mask = Mask((10, 10), cells)

r1 = mask.tomatrix()
r2 = mask.tomatrix()                       

The error says

TypeError: unhashable type: 'Mask'

as if lru_cache tries to cache self in self.tomatrix().

On the other hand, if I don't subclass from dict and instead have an internal member self.data that stores the actual data, the LRU wrapper does not complain.

Code that works:

from functools import lru_cache
import numpy as np

class Mask:
    def __init__(self, shape, data = None):
        self.data = data
        self.shape = shape

    @lru_cache(maxsize=1)
    def tomatrix(self):
        dense = np.zeros(self.shape[0] * self.shape[1])
        for uid, entries in self.data.items():
            dense[entries] = uid
        return np.reshape(dense, self.shape)

cells = {i : np.arange(i * 10, (i + 1) * 10) for i in range(10)}
mask = Mask((10, 10), cells)

r1 = mask.tomatrix()
r2 = mask.tomatrix()                       

Anyone can help me figure out this mystery? I'd like to continue subclassing from dict and I need to use LRU caching.

1 Answers

The solution is to get the default __hash__ function defined for user classes that inherits from object. dict defines its own __eq__, but no __hash__ which is therefore blocked.

Adding the line

__hash__ = object.__hash__

to Mask(dict) works.

Many thanks to this answer to another question!

Related