Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A dict-like class that uses transformed keys

I'd like a dict-like class that transparently uses transformed keys on lookup, so that I can write

k in d          # instead of f(k) in d
d[k]            # instead of d[f(k)]
d.get(k, v)     # instead of d.get(f(k), v)

etc. (Imagine for example that f does some kind of canonicalization, e.g. f(k) returns k.lower().)

It seems that I can inherit from dict and override individual operations, but not that there is a centralized spot for such transformation that all keys go through. That means I have to override all of __contains__, __getitem__, get, and possibly __missing__, etc. This gets too tedious and error-prone, and not very attractive unless this overhead outweighs that of manually substituting f(k) for every call on a plain dict.

like image 309
musiphil Avatar asked Oct 31 '22 12:10

musiphil


1 Answers

Well, the idiomatic way to do it is probably using dimo414's answer. For the case where the transform is not pure (do not always evaluates the same result value given the same argument):

class Foo(dict):
    def __init__(self, transform, *args, **kwargs):
        super(Foo, self).__init__(self, *args, **kwargs)
        assert isfunction(transform), u'Transform argument must be a function.'
        self._transform = transform
    def get(self, k, d=None):
        return super(Foo, self).get(self._transform(k), d)
    def __getitem__(self, item):
        return super(Foo, self).__getitem__(self._transform(item))
    def __contains__(self, item):
        return super(Foo, self).__contains__(self._transform(item))
    def __repr__(self):
        return '<Foo instance {}>'.format(id(self))

Testing:

>>> import datetime
>>> # {0: '0', 1: '1', 2: '2' ... 99: '99'}
>>> x = Foo(lambda x: (datetime.datetime.now() - x).seconds, ((i, str(i)) for i in range(10)))
>>> t = datetime.datetime.now()
>>> x.get(t)
'5'
>>> x[t]
'12'

Not that tedious but I don't like how it smells (in terms of design).

like image 132
Paulo Scardine Avatar answered Nov 11 '22 08:11

Paulo Scardine