[Python-ideas] Dict with inverse form
Ian Kelly
ian.g.kelly at gmail.com
Fri Feb 12 15:50:40 EST 2016
On Fri, Feb 12, 2016 at 1:36 PM, Chris Barker <chris.barker at noaa.gov> wrote:
> On Fri, Feb 12, 2016 at 12:27 PM, Andrew Barnert via Python-ideas
> <python-ideas at python.org> wrote:
>>
>> I look forward to it. Next time I need some variation of this, even if it
>> *isn't* the same variation you end up building, the fact that there's a
>> potential de facto standard for what to call the ".inv" or whatever still
>> helps me, right?
>
>
> yeah, though I'm not sure I like that name... (can't think of a better one
> right now, though).
>
> But what I would like is for the "inverse" to be available as an object
> itself, so:
>
> my_double_dict = DoubleDict( ( (1:'a'), (2:'b'), (3:'c) ) )
> my_inverse = my_double_dict.inv
>
> my_double_dict[1] == 'a'
> my_inverse['a'] == 1
>
>
> i.e you now have two objects, which are essentially the same object, but
> with inverse referencing semantics.
I have some unpublished (lightly tested) code that basically does this
(inspired by Guava's BiMap).
class BiDict(dict):
def __init__(self, *args, **kwargs):
self._inverse = _BiDictInverse(self)
self._super_inverse = super(BiDict, self._inverse)
self.update(*args, **kwargs)
@property
def inverse(self):
return self._inverse
def __repr__(self):
return 'BiDict(%s)' % super().__repr__()
def __setitem__(self, key, value):
if value in self._inverse and self._inverse[value] != key:
raise ValueError(
'%r already bound to %r' % (value, self._inverse[value]))
if key in self:
self._super_inverse.__delitem__(self[key])
super().__setitem__(key, value)
self._super_inverse.__setitem__(value, key)
def __delitem__(self, key):
self._super_inverse.__delitem__(self[key])
super().__delitem__(key)
def clear(self):
super().clear()
self._super_inverse.clear()
def pop(self, key, *args):
key_was_present = key in self
value = super().pop(key, *args)
if key_was_present:
self._super_inverse.__delitem__(value)
return value
def popitem(self):
(key, value) = super().popitem()
self._super_inverse.__delitem__(value)
return (key, value)
def setdefault(self, key, *args):
key_was_present = key in self
value = super().setdefault(key, *args)
if not key_was_present:
self._super_inverse.__setitem__(value, key)
return value
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError(
'update expected at most 1 arguments, got %d' % len(args))
if args and hasattr(args[0], 'keys'):
for key in args[0]:
self[key] = args[0][key]
elif args:
for key, value in args[0]:
self[key] = value
for key in kwargs:
self[key] = kwargs[key]
class _BiDictInverse(BiDict):
def __init__(self, forward_dict):
self._inverse = forward_dict
self._super_inverse = super(BiDict, self._inverse)
More information about the Python-ideas
mailing list