[Solved] python mapping that stays sorted by value


The following class is a quick and dirty implementation with no guarantees for efficiency but it provides the desired behavior.
Should someone provide a better answer, I will gladly accept it!

class SortedValuesDict:
    def __init__(self, args=None, **kwargs):
        """Initialize the SortedValuesDict with an Iterable or Mapping."""
        from collections import Mapping
        from sortedcontainers import SortedSet
        self.__sorted_values = SortedSet()
        self.__key_value_pairs = {}
        self.__value_key_pairs = {}
        if args is not None:
            iterable = args.items() if isinstance(args, Mapping) else args
            for key, value in iterable:
                self.__setitem__(key, value)
        for key, value in kwargs.items():
            self.__setitem__(key, value)

    def __repr__(self):
        """Get a representation of the SortedValuesDict."""
        return f"SortedValuesDict({', '.join(f'{k}: {v}' for k, v in self)})"

    def __setitem__(self, key, value):
        """Store the key, value pair in the SortedValuesDict."""
        if key in self:
            del self[key]
        self.__key_value_pairs[key] = value
        self.__sorted_values.add(value)
        if value in self.__value_key_pairs:
            self.__value_key_pairs[value].add(key)
        else:
            self.__value_key_pairs[value] = {key}

    def __getitem__(self, key):
        """Get the value corresponding to the key."""
        return self.__key_value_pairs[key]

    def __delitem__(self, key):
        """Remove the given key from the SortedValuesDict"""
        value = self.__key_value_pairs.pop(key)
        keys = self.__value_key_pairs[value]
        keys.remove(key)
        if not keys:
            del self.__value_key_pairs[value]
            self.__sorted_values.remove(value)

    def __contains__(self, key):
        """Test if key is in SortedValuesDict."""
        return key in self.__key_value_pairs

    def __iter__(self):
        """Iterate over the SortedValuesDict items."""
        for value in self.__sorted_values:
            for key in self.__value_key_pairs[value]:
                yield key, value

    def keys(self):
        """Iterate over the SortedValuesDict keys."""
        for value in self.__sorted_values:
            for key in self.__value_key_pairs[value]:
                yield key

    def values(self):
        """Iterate over the SortedValuesDict values."""
        for value in self.__sorted_values:
            for key in self.__value_key_pairs[value]:
                yield value

    def items(self):
        """Iterate over the SortedValuesDict items."""
        yield from self

    def peek(self, pos=0):
        """Peek at the next key for the value at position pos."""
        return next(iter(self.__value_key_pairs[self.__sorted_values[pos]]))

    def peekvalue(self, pos=0):
        """Peek at the value at position pos (default 0)."""
        return self.__sorted_values[pos]

    def peekitem(self, pos=0):
        """Peek at the the next key for the value at position pos."""
        value = self.__sorted_values[pos]
        return next(iter(self.__value_key_pairs[value])), value

    def pop(self, pos=-1):
        """Pop a key corresponding to the value at position pos."""
        value = self.__sorted_values[pos]
        keys = self.__value_key_pairs[value]
        key = keys.pop()
        del self.__key_value_pairs[key]
        if not keys:
            del self.__value_key_pairs[self.__sorted_values.pop(pos)]
        return key

    def popitem(self, pos=-1):
        """Pop an item corresponding to the value at position pos."""
        value = self.__sorted_values[pos]
        keys = self.__value_key_pairs[value]
        key = keys.pop()
        del self.__key_value_pairs[key]
        if not keys:
            del self.__value_key_pairs[self.__sorted_values.pop(pos)]
        return key, value
m = SortedValuesDict(a=3, b=1, c=2)
[*m.values()]  # [1, 2, 3]
[*m]  # [('b', 1), ('c', 2), ('a', 3)] (same as [*m.items()])
m.peekitem(0)  # ('b', 1)
m.peekitem(-1)  # ('a', 3)
m['d'] = 0
m.peekitem(0)  # ('d', 0)
m.peekitem(-1)  # ('a', 3)
m['e'] = 0
m  # SortedValuesDict(d: 0, e: 0, b: 1, c: 2, a: 3)
m.peekitem(0)  # (('d', 0), ('e', 0))
m.pop()  # 'a' (from back)
m.pop(0)  # 'd'
m  # SortedValuesDict(e: 0, b: 1, c: 2)
m.popitem()  # ('c', 2)

solved python mapping that stays sorted by value