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.
피터 우드

답변:


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

감사! 나는이 "할 수있는"방법은 모든 스와핑 사용할 때, 나는 모두 주문을 시도 ... 관심 부족 맹세 할 수있는 super명시 적 위해의를 dicts는 다음을 제외하고 작업에 나타납니다 len돌아갑니다 0. 어디에서 왔어요?
Paul Panzer

2
__len _ () __ 의 super () 호출은 mro 에서 다음으로 이동합니다 (D, MutableMapping, dict). 즉, 항상 0을 반환 하는 MutableMappiing .__ len __ () 메서드입니다. 직접 호출 할 의도는 없었으며 항상 재정의해야합니다. 그렇기 때문에 dict.__len__(self)직접 전화해야 합니다. 그리고 이것이 제가 "이것이 제대로 작동하지 않을 것"이라고 말한 이유 중 하나입니다. ;-)
Raymond Hettinger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.