On Feb 12, 2016, at 15:59, Chris Barker <chris.barker@noaa.gov> wrote:

On Fri, Feb 12, 2016 at 3:02 PM, Nikolaus Rath <Nikolaus@rath.org> wrote:

Are there use cases where the set of keys and the set of values is
overlapping?

In most cases you can probably get away by not offering an "inverse"
object at all, just let regular lookup fall back on value lookup.

hmm, interesting. my use case at hand is mapping color name strings to RGB values -- it should be a one to one relationship, and I need to look for either one.

But you are quite right, they would never overlap, unless someone decided a nice name for a color would be "(125, 15, 235)" -- even then they wouldn't because the RGB value sare a tuple, not a string.

So that may be the way to get the easiest interface.

If that's your use case, there's an even easier interface: just toss the forward and reverse mappings in the same dict. If you don't need to iterate the dict[^1] or print in user-friendly form, it's hard to beat that for simplicity or compactness.[^2]

Of the top of my head (untested):

    class SelfInvertingDict(dict):
        def __delitem__(self, key):
            super().__delitem__(self[key])
            super().__delitem__(key)
        def __setitem__(self, key, value):
            if value in self and self[value] != key:
                raise ValueError("duplicate key: '{}'".format(value)
            if key in self: del self[key]
            super().__setitem__(key, value)
            super().__setitem__(value, key)


  [^1]: Even if you do need to iterate, you can always do "(or don't mind iterating with a type switch like "names = (key for key in d if isinstance(key, str))"

  [^2]: On the other hand, it's pretty easy to beat for static-type-checking purposes. The actual type of that dual dict is pretty ugly, and probably not describable in mypy terms, so presumably you'd just punt and label it Dict[Any, Any], or at best stick Union types in for key and value. But that's true for the double-dict-with-fall-through design too.