Search code examples
pythonpython-3.xdictionarykeyextending

How to properly override method keys() while extending dict (in python3)


EDIT: I added the class and some info, see the new post.

I have created a class that extends dict and set (more exactly, I extended collections.abc.MutableMapping and MutableSet). Now I want to properly override the method keys().

This class represents a set of other objects, called Measurand:

class Measurands(MutableSet, MutableMapping):
    @property
    def measurands(self):
        return self._measurands
    
    
    @measurands.setter
    def measurands(self, val):
        self._measurands = []
        
        for el in val:
            self.add(el)
    
    
    def __init__(self, arg=None):
        if arg is None:
            self.measurands = []
        else:
            try:
                measurands = arg.measurands
            except AttributeError:
                if isinstance(arg, Iterable):
                    measurands = list(arg)
                else:
                    measurands = [arg]
            
            self.measurands = measurands
            
    
    def __iter__(self):
        return iter(self.measurands)
    
    
    def __getitem__(self, key):
        for m in self:
            if m.id == key:
                return m
    
        raise KeyError(key)
    
    
    def __contains__(self, el_x):
        is_in = False
        
        for el in self:
            if el_x.id == el.id:
                is_in = True
                break
        
        return is_in
    
    
    def add(self, el):
        try:
            self[el.id].session_ids |= el.session_ids
        except KeyError:
            self[el.id] = Measurand(el)
    
    
    def _discard(self, key):
        res = False
        
        for i, m in enumerate(self):
            if m.id == key:
                del self.measurands[i]
                res = True
                break
        
        return res
    
    
    def __delitem__(self, key):
        res = self._discard(self, key)
        
        if not res:
            raise KeyError(key)
    
    def discard(self, key):
        self._discard(self, key)
    
    
    def remove(self, key):
        self.__delitem__(self, key)
    
    
    def __len__(self):
        return len(self.measurands)
    
    
    def __setitem__(self, key, value):
        res = False
        value_true = Measurand(value)
        
        for i, m in enumerate(self):
            if m.id == key:
                res = True
                
                if value.id == key:
                    self.measurands[i] = value_true
                else:
                    raise KeyError(key)
                
                break
        
        if not res:
            self.measurands.append(value_true)
    
    
    def __str__(self):
        string = "Measurands({"
        
        for m in self:
            string += str(m)
            string += ", "
        
        if string:
            string = string[:-2]
        
        string += "})"
        
        return string

The problem is the default method keys returns a "list" of Measurand objects. This is not what I want. The keys should be the property id of the Measurand objects.

For now I'm returning a simple list, but I would return a dict_keys or a collection.abc.KeysView object. Unluckily, dict_keys is not a global name.

  1. Where is dict_keys?
  2. Is this the correct/pythonic way to do it?

Solution

  • This is already handled for you! collections.abc.MutableMapping includes a reasonable default implementation of keys that provides the same interface as dict.keys.

    Simply delete your keys method, and you'll be using the inherited implementation.