サブクラスでdict.update()メソッドをオーバーライドして、dictキーの上書きを防ぐ 質問する

サブクラスでdict.update()メソッドをオーバーライドして、dictキーの上書きを防ぐ 質問する

今日、私は「Python 辞書の理解がキーを上書きするとエラーが発生します」と尋ね、答えを試してみることにしました。自然に思いついた方法は、サブクラス化することでしたdict。しかし、答えに行き詰まってしまい、今では自分で解決することに夢中になっています。

ノート:

  • いいえ、この質問への回答を他の質問への回答として提出する予定はありません。
  • namedtuple現時点では、これは私にとって純粋に知的な訓練です。実際問題として、このようなことが必要な場合は、ほぼ間違いなく または通常の辞書を使用します。

私の(うまく機能していない)解決策:

class DuplicateKeyError(KeyError):
    pass



class UniqueKeyDict(dict):
    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)


    def __setitem__(self, key, value):
        if key in self:  # Validate key doesn't already exist.
            raise DuplicateKeyError('Key \'{}\' already exists with value \'{}\'.'.format(key, self[key]))
        super().__setitem__(key, value)


    def update(self, *args, **kwargs):
        if args:
            if len(args) > 1:
                raise TypeError('Update expected at most 1 arg.  Got {}.'.format(len(args)))
            else:
                try:
                    for k, v in args[0]:
                        self.__setitem__(k, v)
                except ValueError:
                    pass

        for k in kwargs:
            self.__setitem__(k, kwargs[k])

私のテストと期待される結果

>>> ukd = UniqueKeyDict((k, int(v)) for k, v in ('a1', 'b2', 'c3', 'd4'))  # Should succeed.
>>> ukd['e'] = 5  # Should succeed.
>>> print(ukd)
{'a': 1, 'b': 2, 'c': 3, d: 4, 'e': 5}
>>> ukd['a'] = 5  # Should fail.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __setitem__
__main__.DuplicateKeyError: Key 'a' already exists with value '1'.
>>> ukd.update({'a': 5})  # Should fail.
>>> ukd = UniqueKeyDict((k, v) for k, v in ('a1', 'b2', 'c3', 'd4', 'a5'))  # Should fail.
>>>

問題は私のupdate()やり方にあると確信していますが、何が間違っているのかを正確に判断できません。

以下は、私の方法のオリジナル バージョンですupdate()。このバージョンは、辞書にすでに存在するキー/値のペアを呼び出すときに重複があると予想どおりに失敗しますmy_dict.update({k: v})が、元の辞書の作成中に重複キーを含めると失敗しません。これは、引数を に変換すると、dict辞書のデフォルトの動作、つまり重複キーが上書きされるためです。

def update(self, *args, **kwargs):
    for k, v in dict(*args, **kwargs).items():
        self.__setitem__(k, v)

ベストアンサー1

興味深いのは、の__setitem__の動作を変更するには、単にオーバーライドするだけでは不十分だということです。を使用して が更新されるときに、がその メソッドを使用することを期待していました。すべてのケースにおいて、に触れることなく目的の結果を達成するには、を実装する方がよいと思います。updatedictdict__setitem__updatecollections.MutableMappingupdate

import collections

class UniqueKeyDict(collections.MutableMapping, dict):

    def __init__(self, *args, **kwargs):
        self._dict = dict(*args, **kwargs)

    def __getitem__(self, key):
        return self._dict[key]

    def __setitem__(self, key, value):
        if key in self:
            raise DuplicateKeyError("Key '{}' already exists with value '{}'.".format(key, self[key]))
        self._dict[key] = value

    def __delitem__(self, key):
        del self._dict[key]

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)

編集:チェックdictを満たすために基本クラスとして含められましたisinstance(x, dict)

おすすめ記事