有没有办法从dict和collections.abc.MutableMapping一起继承子类?


26

为了示例起见,假设我想继承子类dict并将所有键都大写:

class capdict(dict):
    def __init__(self,*args,**kwds):
        super().__init__(*args,**kwds)
        mod = [(k.capitalize(),v) for k,v in super().items()]
        super().clear()
        super().update(mod)
    def __getitem__(self,key):
        return super().__getitem__(key.capitalize())
    def __setitem__(self,key,value):
        super().__setitem__(key.capitalize(),value)
    def __delitem__(self,key):
        super().__detitem__(key.capitalize())

这在一定程度上有效

>>> ex = capdict(map(reversed,enumerate("abc")))
>>> ex
{'A': 0, 'B': 1, 'C': 2}
>>> ex['a']
0

但是,当然,仅适用于我记得要实现的方法,例如

>>> 'a' in ex
False

不是期望的行为。

现在,填充可以从“核心”方法派生的所有方法的惰性方法是混入collections.abc.MutableMapping。只是,它在这里不起作用。我想是因为所讨论的方法(__contains__在示例中)已经由提供dict。

有办法吃我的蛋糕吗?让我们MutableMapping只看一下我已重写的方法,以便在这些方法的基础上重新实现其他方法,是否有些魔术?


您可能不需要使用MutableMapping。请参阅不区分大小写的字典。
— martineau

@martineau谢谢,正如我所说的只是一个例子。
— Paul Panzer

您可以使用os._Environ。
— 彼得·伍德

Answers:


26

您可以做什么:

这可能无法很好地解决(即,不是最干净的设计),但是您可以先从MutableMapping继承,然后再从dict继承。

然后MutableMapping将使用您已实现的任何方法(因为它们是查找链中的第一个方法):

>>> class D(MutableMapping, dict):
        def __getitem__(self, key):
            print(f'Intercepted a lookup for {key!r}')
            return dict.__getitem__(self, key)


>>> d = D(x=10, y=20)
>>> d.get('x', 0)
Intercepted a lookup for 'x'
10
>>> d.get('z', 0)
Intercepted a lookup for 'z'
0

更好的方法:

最干净的方法(易于理解和测试)是从MutableMapping继承,然后使用常规dict作为基础数据存储区(使用组合而不是继承)来实现所需的方法:

>>> class CapitalizingDict(MutableMapping):
        def __init__(self, *args, **kwds):
            self.store = {}
            self.update(*args, **kwds)
        def __getitem__(self, key):
            key = key.capitalize()
            return self.store[key]
        def __setitem__(self, key, value):
            key = key.capitalize()
            self.store[key] = value
        def __delitem__(self, key):
            del self.store[key]
        def __len__(self):
            return len(self.store)
        def __iter__(self):
            return iter(self.store)
        def __repr__(self):
            return repr(self.store)


>>> d = CapitalizingDict(x=10, y=20)
>>> d
{'X': 10, 'Y': 20}
>>> d['x']
10
>>> d.get('x', 0)
10
>>> d.get('z', 0)
0
>>> d['w'] = 30
>>> d['W']
30

谢谢!我可能发誓我尝试过两个命令...出于兴趣,当我使用“可以做”的方法将所有supers换为显式dicts时,它似乎起作用,除了lenreturn 0。那是哪里来的
— Paul Panzer

2
从超()调用__len _()__去的MRO下一: (D, MutableMapping, dict)。这就是MutableMappiing .__ len __()方法,该方法始终返回0。并非旨在直接调用它-始终应将其重写。因此,您必须dict.__len__(self)直接致电。这就是我说“这可能无法很好解决”的原因之一;-)
— Raymond Hettinger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.