A dictate class that uses converted keys

I need a dictate class that transparently uses the converted keys when searching, so 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 .. (Suppose, for example, that it fis performing some canonization, for example, f(k)returns k.lower().)

It seems that I can inherit from dictand redefine individual operations, but not that there is such a centralized place for such a conversion that passes all the keys. This means that I have to redefine all __contains__, __getitem__, getand, possibly, __missing__etc. This becomes too tedious and error-prone and not very attractive, unless these overheads outweigh, t22> for each call to idle dict.

+4
source share
3 answers

I'm not sure why your question is subverted, this is a reasonable thing to want. In Java, Guava provides several map conversion utilities that provide representations on a support map, as you describe. However, they do not provide a method Maps.transformKeys()because it is actually not a very useful function. See How to convert Map <String, String> to Map <Long, String> using guava and Why Guava does not provide a way to convert map keys to find out why.

, . , , , , , dict, , :

{ f(k): v for k, v in d.iteritems() }
+1

, , , dimo414. , ( , ):

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))

:

>>> 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'

, , , ( ).

+1

, dict(), factory, TransformDict, .

def transform_dict(transform_key):
    def _transform_dict(*args, **kwargs):
        return TransformDict(transform_key, *args, **kwargs)
    return _transform_dict

which can be used as:

>>> LowerDict = transform_dict(lambda k: k.lower())
>>> lower_dict = LowerDict({'FOO': 1}, BaR=2)
TransformDict(<function <lambda> at 0x12345678>, {'foo': 1, 'bar': 2})

TransformDictmust implement an MutableMappingabstract base class, so any potentially missing method dictwill not pass silently. All techniques related to key transform can be implemented in terms of __contains__(), __getitem__(), __setitem__()and __delitem__().

import collections
import sys

class TransformDict(collections.MutableMapping):

    def __init__(self, __transform_key, *args, **kwargs):
        self.data = dict(*args, **kwargs)
        self.transform_key = __transform_key

    # Key methods.

    def __contains__(self, key):
        key = self.transform_key(key)
        return key in self.data

    def __getitem__(self, key):
        key = self.transform_key(key)
        return self.data[key]

    def __setitem__(self, key, value):
        key = self.transform_key(key)
        self.data[key] = value

    def __delitem__(self, key):
        key = self.transform_key(key)
        del self.data[key]

    # Operator methods.

    def __iter__(self):
        return iter(self.data)

    def __len__(self):
        return len(self.data)

    def __eq__(self, other):
        if isinstance(other, TransformDict):
          other = other.data
        return self.data == other

    def __ne__(self, other):
        return not (self == other)

    def __repr__(self):
        return "{}({!r}, {!r})".format(self.__class__.__name__, self.transform_key, self.data)

    # Accessor methods.

    def get(self, key, default=None):
        if key in self:
            return self[key]
        return default

    def keys(self):
        return self.data.keys()

    def items(self):
        return self.data.items()

    def values(self):
        return self.data.values()

    if sys.version_info[0] == 2:

      def iterkeys(self):
          return self.data.iterkeys()

      def itervalues(self):
          return self.data.itervalues()

      def iteritems(self):
          return self.data.iteritems()

      def viewkeys(self):
          return self.data.viewkeys()

      def viewvalues(self):
          return self.data.viewvalues()

      def viewitems(self):
          return self.data.viewitems()

    # Mutable methods.

    def clear(self):
        self.data.clear()

    def pop(self, key, default=KeyError):
        if key in self or default is KeyError:
            value = self[key]
            del self[key]
            return value
        return default

    def popitem(self):
        return self.data.popitem()

    def setdefault(self, key, default=None):
      if key not in self:
        self[key] = default
        return default
      return self[key]

    def update(self, other):
        for key for other:
            self[key] = other[key]

    # Miscellaneous methods.

    def copy(self):
        return self.__class__(self.transform_key, self.data)
0
source

Source: https://habr.com/ru/post/1568070/


All Articles