如何“完美"覆盖一个字典?
问题描述
如何使 dict 的子类尽可能完美"?最终目标是拥有一个简单的 dict,其中的键是小写的.
How can I make as "perfect" a subclass of dict as possible? The end goal is to have a simple dict in which the keys are lowercase.
似乎应该有一些我可以重写的小原语来完成这项工作,但根据我所有的研究和尝试,情况似乎并非如此:
It would seem that there should be some tiny set of primitives I can override to make this work, but according to all my research and attempts it seem like this isn't the case:
如果我覆盖
__getitem__
/__setitem__
,那么get
/set
不起作用.我怎样才能让它们工作?当然我不需要单独实现它们?
If I override
__getitem__
/__setitem__
, thenget
/set
don't work. How can I make them work? Surely I don't need to implement them individually?
我是否在阻止酸洗工作,是否需要实现 __setstate__
等?
Am I preventing pickling from working, and do I need to implement __setstate__
etc?
我是否需要repr
、update
和__init__
?
我应该只使用可变映射(似乎不应该使用UserDict
或 DictMixin
)?如果是这样,怎么做?这些文档并不完全有启发性.
Should I just use mutablemapping (it seems one shouldn't use UserDict
or DictMixin
)? If so, how? The docs aren't exactly enlightening.
这是我第一次尝试,get()
不起作用,毫无疑问还有许多其他小问题:
Here is my first go at it, get()
doesn't work and no doubt there are many other minor problems:
class arbitrary_dict(dict):
"""A dictionary that applies an arbitrary key-altering function
before accessing the keys."""
def __keytransform__(self, key):
return key
# Overridden methods. List from
# https://stackoverflow.com/questions/2390827/how-to-properly-subclass-dict
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
# Note: I'm using dict directly, since super(dict, self) doesn't work.
# I'm not sure why, perhaps dict is not a new-style class.
def __getitem__(self, key):
return dict.__getitem__(self, self.__keytransform__(key))
def __setitem__(self, key, value):
return dict.__setitem__(self, self.__keytransform__(key), value)
def __delitem__(self, key):
return dict.__delitem__(self, self.__keytransform__(key))
def __contains__(self, key):
return dict.__contains__(self, self.__keytransform__(key))
class lcdict(arbitrary_dict):
def __keytransform__(self, key):
return str(key).lower()
解决方案
您可以使用 ABCs(抽象基类)来自 collections.abc
模块.它甚至会告诉您是否错过了某个方法,因此以下是关闭 ABC 的最小版本.
You can write an object that behaves like a dict
quite easily with ABCs (Abstract Base Classes) from the collections.abc
module. It even tells you if you missed a method, so below is the minimal version that shuts the ABC up.
from collections.abc import MutableMapping
class TransformedDict(MutableMapping):
"""A dictionary that applies an arbitrary key-altering
function before accessing the keys"""
def __init__(self, *args, **kwargs):
self.store = dict()
self.update(dict(*args, **kwargs)) # use the free update to set keys
def __getitem__(self, key):
return self.store[self._keytransform(key)]
def __setitem__(self, key, value):
self.store[self._keytransform(key)] = value
def __delitem__(self, key):
del self.store[self._keytransform(key)]
def __iter__(self):
return iter(self.store)
def __len__(self):
return len(self.store)
def _keytransform(self, key):
return key
您可以从 ABC 获得一些免费方法:
You get a few free methods from the ABC:
class MyTransformedDict(TransformedDict):
def _keytransform(self, key):
return key.lower()
s = MyTransformedDict([('Test', 'test')])
assert s.get('TEST') is s['test'] # free get
assert 'TeSt' in s # free __contains__
# free setdefault, __eq__, and so on
import pickle
# works too since we just use a normal dict
assert pickle.loads(pickle.dumps(s)) == s
我不会直接继承 dict
(或其他内置函数).这通常是没有意义的,因为你真正想做的是实现一个dict
的接口.这正是 ABC 的用途.
I wouldn't subclass dict
(or other builtins) directly. It often makes no sense, because what you actually want to do is implement the interface of a dict
. And that is exactly what ABCs are for.
相关文章