类似于dict的类,它使用转换后的键

时间:2014-12-17 23:21:58

标签: python dictionary

我想要一个类似dict的类,它在查找时透明地使用转换后的键,这样我就可以写了

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)

等。 (想象一下,例如f进行某种规范化,例如f(k)返回k.lower()。)

似乎我可以继承dict并覆盖单个操作,但并不是所有密钥都经过这种转换的集中点。这意味着我必须覆盖所有__contains____getitem__get以及可能__missing__等等。这会变得过于繁琐且容易出错,除非非常有吸引力,除非这个开销大于手动用f(k)代替普通dict上的每个电话。

3 个答案:

答案 0 :(得分:1)

我不确定为什么你的问题被低估了,这是一个合理的想法。在Java中,Guava提供了几个映射转换实用程序,可以像您所描述的那样提供对支持映射的视图。但是它们不提供Maps.transformKeys()方法,因为它实际上不是一个非常有用的函数。有关原因的详细信息,请参阅How to convert Map<String, String> to Map<Long, String> using guavaWhy Guava does not provide a way to transform map keys

简而言之,在一般情况下不可能有效地提供密钥转换。不是创建您想象的复杂且可能不一致的数据结构,最好的办法是create a new dict应用您的密钥转换,例如:

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

答案 1 :(得分: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'

不是那么乏味但我不喜欢它的气味(就设计而言)。

答案 2 :(得分:0)

由于您希望保持与dict()完全相同的签名,我 建议创建一个工厂函数来包装TransformDict来提供 相同的签名。

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

可以用作:

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

TransformDict应该实现MutableMapping摘要 基类,以便任何可能遗漏的dict方法都不会通过 默默。所有处理转换密钥的方法都可以 根据{{​​3}},__contains__()执行, __getitem__()__setitem__()

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)