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ありがとう、私が言ったようにそれはほんの一例でした。
ポールパンツァー

使用できますos._Environ
Peter Wood

回答:


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

ありがとう!私は両方の注文を試したことを誓ったかもしれません...興味がないのですが、 "could do"メソッドを使用しsuperて明示的なdictのすべてのを入れ替えると、return を除いて機能するように見えlenます0。どこから来たの?
ポールパンツァー

2
__len _()__からのsuper()呼び出しは、mroの次の呼び出しに進みます (D, MutableMapping, dict)。それは、常に0を返すMutableMappiing .__ len __()メソッドです。これは、直接呼び出すことを意図したものではなく、常にオーバーライドされることになっています。そのため、dict.__len__(self)直接電話する必要があります。そして、それが私が「これはおそらくうまくいかないだろう」と言った理由の1つです;-)
Raymond Hettinger
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.