web-dev-qa-db-ja.com

KeyErrorを発生させる代わりに、ディクショナリにないキーのキーを返すpythonディクショナリを作成する方法は?

pythonディクショナリを作成します。このディクショナリには、キーのキー値が返されません。

使用例:

dic = smart_dict()
dic['a'] = 'one a'
print(dic['a'])
# >>> one a
print(dic['b'])
# >>> b
54
sorin

dictsには __missing__ このためのフック:

class smart_dict(dict):
    def __missing__(self, key):
        return key
62
Jochen Ritzel

なぜ使用しないのですか

dic.get('b', 'b')

確かに、他の人が指摘するようにdictをサブクラス化できますが、getにデフォルト値を設定できることを時々思い出すと便利です。

defaultdictを試してみたい場合は、これを試してください:

dic = defaultdict()
dic.__missing__ = lambda key: key
dic['b'] # should set dic['b'] to 'b' and return 'b'

例外...まあ:AttributeError: ^collections.defaultdict^object attribute '__missing__' is read-onlyなので、サブクラス化する必要があります。

from collections import defaultdict
class KeyDict(defaultdict):
    def __missing__(self, key):
        return key

d = KeyDict()
print d['b'] #prints 'b'
print d.keys() #prints []
25
Daren Thomas

最初の回答者はdefaultdictについて言及しましたが、__missing__dictのサブクラスの場合:

>>> class Dict(dict):
        def __missing__(self, key):
            return key


>>> d = Dict(a=1, b=2)
>>> d['a']
1
>>> d['z']
'z'

また、私は2番目の回答者のアプローチが気に入っています。

>>> d = dict(a=1, b=2)
>>> d.get('z', 'z')
'z'
13

おめでとうございます。標準の_collections.defaultdict_型の無用さも発見しました。 コードのにおい のその実行可能な中間ヒープが、あなたの繊細な感性を私のものと同じくらい気分を害する場合、これは幸運なStackOverflowの日です。

組み込みのtype()の-​​ -parameterバリアント の禁止された驚異のおかげで、役に立たないデフォルトの辞書タイプを作成することは楽しくて有益です。

Dict .__ missing __()の何が問題になっていますか?

あなたが過剰なボイラープレートと_collections.defaultdict_の衝撃的な愚かさが好きであると仮定すると、絶対に何もありません–これはshouldは期待どおりに動作しますが、実際には動作しません。公平を期すために、サブクラスdictの実装 オプション__missing__()メソッドJochen Ritzel受け入れられる解 = is単一のデフォルトディクショナリのみを必要とする小規模なユースケースの素晴らしい回避策。

しかし、この種のボイラープレートはスケーリングが不十分です。欠落しているキーと値のペアを生成するための独自のわずかに異なるロジックを持つ複数のデフォルト辞書をインスタンス化していることに気付いた場合、産業用の強度の代替自動化ボイラープレートが必要です。

または少なくともニース。壊れたものを修正しないのはなぜですか?

DefaultDictの紹介

純粋なPython(docstrings、comments、および空白文字を除く)の10行未満で、ユーザー定義の呼び出し可能オブジェクトで初期化されたDefaultDictタイプを定義し、欠落のデフォルト値を生成しますキー。標準の_collections.defaultdict_タイプに渡される呼び出し可能オブジェクトはnoパラメータを無意味に受け入れますが、DefaultDictタイプに渡される呼び出し可能オブジェクトは次の2つのパラメータを有効に受け入れます。

  1. この辞書の現在のインスタンス。
  2. デフォルト値を生成するための現在欠落しているキー。

このタイプの場合、 sorin の質問を解決すると、Pythonの1行に削減されます。

_>>> dic = DefaultDict(lambda self, missing_key: missing_key)
>>> dic['a'] = 'one a'
>>> print(dic['a'])
one a
>>> print(dic['b'])
b
_

正気。 ついに。

コードまたはそれが起こらなかった

_def DefaultDict(keygen):
    '''
    Sane **default dictionary** (i.e., dictionary implicitly mapping a missing
    key to the value returned by a caller-defined callable passed both this
    dictionary and that key).

    The standard :class:`collections.defaultdict` class is sadly insane,
    requiring the caller-defined callable accept *no* arguments. This
    non-standard alternative requires this callable accept two arguments:

    #. The current instance of this dictionary.
    #. The current missing key to generate a default value for.

    Parameters
    ----------
    keygen : CallableTypes
        Callable (e.g., function, lambda, method) called to generate the default
        value for a "missing" (i.e., undefined) key on the first attempt to
        access that key, passed first this dictionary and then this key and
        returning this value. This callable should have a signature resembling:
        ``def keygen(self: DefaultDict, missing_key: object) -> object``.
        Equivalently, this callable should have the exact same signature as that
        of the optional :meth:`dict.__missing__` method.

    Returns
    ----------
    MappingType
        Empty default dictionary creating missing keys via this callable.
    '''

    # Global variable modified below.
    global _DEFAULT_DICT_ID

    # Unique classname suffixed by this identifier.
    default_dict_class_name = 'DefaultDict' + str(_DEFAULT_DICT_ID)

    # Increment this identifier to preserve uniqueness.
    _DEFAULT_DICT_ID += 1

    # Dynamically generated default dictionary class specific to this callable.
    default_dict_class = type(
        default_dict_class_name, (dict,), {'__missing__': keygen,})

    # Instantiate and return the first and only instance of this class.
    return default_dict_class()


_DEFAULT_DICT_ID = 0
'''
Unique arbitrary identifier with which to uniquify the classname of the next
:func:`DefaultDict`-derived type.
'''
_

キー ...取得、キー この難解なウィザードの呼び出しは、type()ビルトインの -パラメータバリアント の呼び出しです。

_type(default_dict_class_name, (dict,), {'__missing__': keygen,})
_

この1行で、新しいdictサブクラスが動的に生成され、オプションの___missing___メソッドが呼び出し元定義の呼び出し可能オブジェクトにエイリアスされます。ボイラープレートの明確な欠如に注意し、DefaultDictの使用をPythonの1行に減らします。

悪質な勝利のための自動化。

7
Cecil Curry

これは簡単にできること、そして何らかの方法で欠損値を変換するさまざまなデフォルトや関数を簡単に設定できることにも同意します。

Cecil Curryanswer に触発されて、私は自分自身に尋ねました。常に異なるクラスを生成していますか?実演させてください:

# default behaviour: return missing keys unchanged
dic = FlexDict()
dic['a'] = 'one a'
print(dic['a'])
# 'one a'
print(dic['b'])
# 'b'

# regardless of default: easy initialisation with existing dictionary
existing_dic = {'a' : 'one a'}
dic = FlexDict(existing_dic)
print(dic['a'])
# 'one a'
print(dic['b'])
# 'b'

# using constant as default for missing values
dic = FlexDict(existing_dic, default = 10)
print(dic['a'])
# 'one a'
print(dic['b'])
# 10

# use callable as default for missing values
dic = FlexDict(existing_dic, default = lambda missing_key: missing_key * 2)
print(dic['a'])
# 'one a'
print(dic['b'])
# 'bb'
print(dic[2])
# 4

どのように機能しますか?それほど難しくありません:

class FlexDict(dict):
    '''Subclass of dictionary which returns a default for missing keys.
    This default can either be a constant, or a callable accepting the missing key.
    If "default" is not given (or None), each missing key will be returned unchanged.'''
    def __init__(self, content = None, default = None):
        if content is None:
            super().__init__()
        else:
            super().__init__(content)
        if default is None:
            default = lambda missing_key: missing_key
        self.default = default # sets self._default

    @property
    def default(self):
        return self._default

    @default.setter
    def default(self, val):
        if callable(val):
            self._default = val
        else: # constant value
            self._default = lambda missing_key: val

    def __missing__(self, x):
        return self.default(x)

もちろん、初期化後にデフォルト関数を変更できるようにするかどうかは議論できますが、これは単に@default.setterを削除し、そのロジックを__init__に吸収することを意味します。

現在の(定数の)デフォルト値へのイントロスペクションを有効にするには、2つの行を追加します。

2
Axel
0
seb