1. ホーム
  2. パイソン

[解決済み】dictを「完璧に」オーバーライドするには?

2022-04-20 21:14:13

質問

のサブクラスを作るにはどうしたらいいですか? ディクショナリー ということです。最終的なゴールは、シンプルな ディクショナリー で、キーは小文字である。

これを動作させるためにオーバーライドできるプリミティブの小さなセットがあるはずだと思うのですが、私のすべての調査と試みによると、そうではなさそうなのです。

  • もし私が オーバーライド __getitem__ / __setitem__ であれば get / set が動作しない。 どうすれば動くようになるのでしょうか? 確かに、私はそれらを個別に実装する必要はないのでしょうか?

  • ピックリングが機能しないように、私は実装する必要があるのでしょうか? __setstate__ などがありますか?

  • 私は 必要 repr , update__init__ ?

  • を使用すればよいのでしょうか? mutablemappingを使用する (を使うべきではないようです)。 UserDict または DictMixin )? もしそうなら、どのように?ドキュメントを読んでもよくわからないのですが。

以下は、私の初体験です。 get() は動作しませんし、他にも細かい問題がたくさんあるのは間違いありません。

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()

解決方法は?

のような振る舞いをするオブジェクトを書けばいいのです。 dict を使うと非常に簡単です。 ABC の抽象基底クラス(Abstract Base Classes)です。 collections.abc モジュールです。 メソッドを見逃した場合にも教えてくれるので、以下はABCをシャットアウトする最小限のバージョンです。

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の無料メソッドをいくつか取得します。

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の目的なのです。