Чи є спосіб підкласифікації з dict і collection.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 дякую, як я вже сказав, що це був лише приклад.
Пол Панцер

Ви можете використовувати os._Environ.
Пітер Вуд

Відповіді:


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, то, здається, він працює, за винятком lenповернень 0. Звідки це походить?
Пол Панцер

2
Супер () виклик від __len _ () __ переходить до наступного в mro : (D, MutableMapping, dict). Це метод MutableMappiing .__ len __ (), який завжди повертає 0. Його не потрібно було називати безпосередньо - його завжди слід переосмислити. Ось чому вам доведеться телефонувати dict.__len__(self)безпосередньо. І це одна з причин того, що я сказав, що "це, ймовірно, не вийде добре" ;-)
Реймонд Хеттінгер,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.