我想要一个类似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
上的每个电话。
答案 0 :(得分:1)
我不确定为什么你的问题被低估了,这是一个合理的想法。在Java中,Guava提供了几个映射转换实用程序,可以像您所描述的那样提供对支持映射的视图。但是它们不提供Maps.transformKeys()
方法,因为它实际上不是一个非常有用的函数。有关原因的详细信息,请参阅How to convert Map<String, String> to Map<Long, String> using guava和Why 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)