属性のようなdictキーにアクセスしますか?


303

obj.foo代わりにdictキーにアクセスする方が便利だと思うobj['foo']ので、次のスニペットを書きました。

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

ただし、Pythonがそのままではこの機能を提供しない理由がいくつかあるに違いないと私は思います。この方法でdictキーにアクセスする際の注意点と落とし穴は何でしょうか?


16
どこでも固定サイズの制限されたセットからハードコードされたキーにアクセスしている場合は、これらを保持するオブジェクトを作成する方がよいでしょう。collections.namedtupleこれには非常に便利です。

6
stackoverflow.com/questions/3031219/…は同様の解決策を持っていますが、さらに一歩進んでいます
keflavich

1
github.com/bcj/AttrDictでこのモジュールを見つけました。ここと関連する質問のソリューションとどのように比較するのかわかりません。
マットウィルキー2014年

また、私は今、私が使用し、同様のハックを使用easydict.EasyDict
ミュオン

「。」を使用して辞書のメンバーにアクセスするその他の方法 :stackoverflow.com/questions/2352181/...
ペールブルードット

回答:


304

これを行う最良の方法は次のとおりです。

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

一部の長所:

  • それは実際に機能します!
  • 隠されているディクショナリクラスメソッドはありません(たとえば、.keys()うまく機能します。もちろん、値を割り当てない限り、以下を参照してください)。
  • 属性とアイテムは常に同期しています
  • 属性が正しく発生するAttributeError代わりに、存在しないキーにアクセスしようとするとKeyError

短所:

  • のようなメソッド.keys()は、着信データによって上書きされるとうまく機能しません
  • 原因メモリリークでPythonの<2.7.4 /のpython3 <3.2.3
  • Pylintがでバナナを行くE1123(unexpected-keyword-arg)と、E1103(maybe-no-member)
  • 初心者にとっては、それは純粋な魔法のようです。

これがどのように機能するかについての簡単な説明

  • すべてのpythonオブジェクトは、内部的にその属性をという名前の辞書に格納します__dict__
  • 内部ディクショナリ__dict__が「単なる辞書」である必要があるという要件はないので、のサブクラスをdict()内部ディクショナリに割り当てることができます。
  • この場合、AttrDict()インスタンス化しているインスタンスを割り当てるだけです(のように__init__)。
  • super()__init__()メソッドを呼び出すことにより、その関数がすべてのディクショナリのインスタンス化コードを呼び出すため、(すでに)ディクショナリとまったく同じように動作することを確認しました。

Pythonがそのままではこの機能を提供しない理由の1つ

「cons」リストに記載されているように、これは、格納されたキーの名前空間(任意のデータや信頼できないデータから取得される可能性があります)と組み込みのdictメソッド属性の名前空間を組み合わせます。例えば:

d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

1
次のような単純なオブジェクトでメモリリークが発生すると思いますか?>>> class MyD(object):... def init __(self、d):... self .__ dict = d
Rafe

2.7
pi

1
それが私が使用しているものなので、それを<= 2.7.3にします。
pi。

1
2.7.4リリースノートでは、修正されたと述べています(以前ではありません)。
Robert Siemer 2015

1
@viveksinghggitsは.、を介してアクセスしているという理由だけで、言語の規則を破ることはできません:)そしてAttrDict、スペースを含むフィールドを自動的に別のフィールドに変換したくありません。
ユリク

125

配列表記を使用する場合、キーの一部としてすべての有効な文字列文字を使用できます。例えば、obj['!#$%^&*()_']


1
@Izkataはい。SEについておもしろいことは、通常「トップの質問」、つまり タイトル、および「一番下の質問」。おそらくSEが「タイトルがすべてを語る」とは聞きたくないためです。ここで「警告」が一番下にあります。
n611x007 2014

2
ないJavaScriptは言語プログラミングの特に良い例ですが、JS内のオブジェクトは、属性アクセスと一般的なケースのための便利なことができます配列表記、両方サポートしていることと、法的な属性名でないシンボルのための一般的なフォールバックを。
アンドレ・キャノン

@Izkataこれは質問にどのように答えますか。この答えは、キーには任意の名前を付けることができるというだけです。
Melab

4
@Melab質問はWhat would be the caveats and pitfalls of accessing dict keys in this manner?(属性として)あり、答えは、ここに表示されているほとんどの文字は使用できないということです。
イズカタ2017年

83

この他のSOの質問から、既存のコードを簡素化する優れた実装例があります。どうですか:

class AttributeDict(dict): 
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

多くのより簡潔には、あなたに入る余分な嫌なもののための任意の余地を残していないと__getattr__し、__setattr__将来的に機能しています。


このメソッドを使用してAttributeDict.updateまたはAttributeDict.getを呼び出すことができますか?
Dor

13
実行時に新しい属性を追加した場合、それらはdict自体ではなくdict属性に追加されることに注意してください。例えばd = AttributeDict(foo=1)d.bar = 1bar属性はdict属性内に格納されますが、dict自体には格納されません。印刷でdはfooアイテムのみが表示されます。
P3trus 2012年

7
+1は、私の知る限り完全に機能するためです。@ GringoSuave、@ Izkata、@ P3trus失敗したと主張する人に、機能しない例を表示するように要求するd = AttributeDict(foo=1);d.bar = 1;print d=>機能する{'foo': 1, 'bar': 1}
デイブアブラハム2013

4
@DaveAbrahams読むフル Hery、ライアン、そしてTheCommunistDuckによって解答の質問と表情。これを行う方法についてではなく、発生する可能性のある問題について尋ねています
イズカタ

6
指定された属性が存在しない場合に__getattr__発生するメソッドを提供する必要AttributeErrorがあります。それ以外の場合は機能しgetattr(obj, attr, default_value)ません(つまり、に存在しないdefault_value場合attrは戻りませんobj
jcdude

83

ここで、尋ねられた質問に答えます

なぜPythonはそのままでは提供しないのですか?

私はそれがZen of Pythonに関係しているのではないかと疑っています。これは、辞書からのアクセス値には2つの明確な方法を作成します。obj['key']obj.key

警告と落とし穴

これらには、コードの明確性の欠如と混乱の可能性が含まれます。つまり、次のコードは、後日コードを保守する他のユーザーを混乱させる可能性があります。しばらくの間コードに戻らないと、混乱を招く可能性があります。繰り返しますが、Zenから:「読みやすさが重要です!」

>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1

場合はd、インスタンス化されるか、 KEY定義されているか、 d[KEY]遠く離れた場所から割り当てられているd.spam使用されている、これは一般的に使用されるイディオムではないので、それは簡単に、行われているものについての混乱につながることができます。私が混乱する可能性があることは知っています。

さらに、の値をKEY次のように変更した場合(ただし、を変更しd.spamない場合)、次のようになります。

>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'

IMO、努力する価値はありません。

他の項目

他の人が述べたように、(文字列だけでなく)ハッシュ可能なオブジェクトをdictキーとして使用できます。例えば、

>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>> 

合法ですが

>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
  File "<stdin>", line 1
  d.(2, 3)
    ^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>> 

ではありません。これにより、オブジェクト属性にアクセスする場合にはない、辞書キーの印刷可能な文字またはその他のハッシュ可能なオブジェクトの全範囲にアクセスできます。これにより、Pythonクックブック(Ch。9)のレシピのような、キャッシュされたオブジェクトのメタクラスのような魔法が可能になります。

ここで私は編集します

私はの美学を好むspam.eggs以上spam['eggs'](私はそれがきれいに見えると思いますが)、と私は本当に私が会ったときに、この機能を渇望始めましたnamedtuple。しかし、次のことができるという利便性はそれよりも優先されます。

>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>

これは簡単な例ですが、obj.key記法を使用する場合とは異なる状況(つまり、XMLファイルから設定を読み込む必要がある場合)でディクテーションを使用することがよくあります。他のケースでは、動的なクラスをインスタンス化して、美的な理由でいくつかの属性を平手打ちにしたい場合、読みやすさを向上させるために一貫性のためにdictを使い続けます。

OPが長い間これを満足に解決したと確信していますが、それでもこの機能が必要な場合は、それを提供するパッケージのいずれかをpypiからダウンロードすることをお勧めします。

  • は私がより身近なものです。のサブクラスなdictので、そのすべての機能を利用できます。
  • AttrDictもかなり良いように見えますが、私はそれに慣れていないため、 Bunchほど詳細にソースを調べていません。
  • Addictは積極的に維持され、attrのようなアクセスなどを提供します。
  • ロタレティのコメントで述べたように、バンチは廃止されましたが、マンチと呼ばれるアクティブなフォークがあります。

ただし、彼のコードを読みやすくするために、彼の表記スタイルを混在させないことを強くお勧めします。この表記法を使用する場合は、動的オブジェクトをインスタンス化し、必要な属性を追加して、1日で呼び出す必要があります。

>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}


コメントのフォローアップ質問に回答するために更新します

Elmoはコメント(下記)で次のように尋ねています。

あなたがさらに深く行きたいならどうしますか?(type(...)を参照)

このユースケースを使用したことはありませんが(ここでも、dict一貫性を保つためにを使用する傾向があります)、次のコードが機能します。

>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
...     setattr(d, x, C())
...     i = 1
...     for y in 'one two three'.split():
...         setattr(getattr(d, x), y, i)
...         i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}

1
Bunchは非推奨ですが、アクティブな分岐があります: github.com/Infinidat/munch
Rotareti

@Rotareti-ヘッズアップありがとう!これは私が使っている機能ではないので、気づきませんでした。
Doug R.

あなたがさらに深く行きたいならどうしますか?(type(...)を参照)
Ole Aldric

6
Pythonは、大雨の中で高く保持された倒立傘のようなものです。最初はスマートでファンキーに見えますが、しばらくすると重くなり始め、突然、SEに組み込まれたグルの要素を読み、ペイロード全体が肩にかかった状態に戻ります。まだびしょぬれになっている間、あなたはより軽く感じ、すべてがとてもクリアでリフレッシュされます。
Ole Aldric


19

標準ライブラリから便利なコンテナクラスをプルできます。

from argparse import Namespace

コードビットをコピーする必要がないようにします。標準の辞書へのアクセスはありませんが、本当に必要な場合は簡単に戻すことができます。argparseのコードはシンプルです。

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

    def __contains__(self, key):
        return key in self.__dict__

2
OPによる最初のコメントに対応する標準ライブラリを参照するためのPLUS 1。
Gordon Bean、

4
Pythonはその場合の(Cで実装)高速クラスを含む:types.SimpleNamespace docs.python.org/dev/library/types.html#types.SimpleNamespace
ヌーノアンドレ・

18

__eq__またはなどのメソッドであるキーが必要な場合はどうなりますか__getattr__

また、文字で始まらないエントリは作成できないため0343853、キーとして使用することはできません。

また、文字列を使用したくない場合はどうなりますか?


確かに、または例えばキーとして他のオブジェクト。しかし、私はそれからのエラーを「予想される動作」として分類します-私の質問では、予想外のものにもっと向けていました。
Izz ad-Din Ruhulessin、2011

pickle.dump使用__getstate__
Cees Timmerman、2015

12

タプルはdictキーを使用できます。コンストラクトでタプルにどのようにアクセスしますか?

また、namedtupleは、属性アクセスを介して値を提供できる便利な構造です。


7
名前付きタプルの欠点は、それらが不変であることです。
Izz ad-Din Ruhulessin、2011

10
不変であることはバグではなくタプルの機能であると言う人もいます。
benの作成者

9

それらすべてを支配するために私が書いた小さなPythonクラスであるProdictはどうですか?)

さらに、自動コード補完再帰的なオブジェクトのインスタンス化自動型変換を利用できます。

あなたが要求したとおりに行うことができます:

p = Prodict()
p.foo = 1
p.bar = "baz"

例1:タイプヒント

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

自動コード完了

例2:自動型変換

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>

2
pip経由でpython2にインストールしますが、python2では機能しません
Ant6n

2
@ Ant6nは型注釈のためにPython 3.6+を必要とします
Ramazan Polat

8

一般的には機能しません。すべての有効なdictキーがアドレス可能な属性(「キー」)を作成するわけではありません。したがって、注意する必要があります。

Pythonオブジェクトはすべて基本的に辞書です。だから私は多くのパフォーマンスや他のペナルティがあるとは思いません。


8

これは元の質問には対応していませんが、私のように、この機能を提供するlibを探すときにここで終わる人にとっては役立つはずです。

アディクトそれは、このための素晴らしいlibにあります:https://github.com/mewwts/addictそれは、前の回答で述べた多くの懸念の世話をします。

ドキュメントの例:

body = {
    'query': {
        'filtered': {
            'query': {
                'match': {'description': 'addictive'}
            },
            'filter': {
                'term': {'created_by': 'Mats'}
            }
        }
    }
}

常習者と:

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'

8

Pythonエコシステムでの「dict keys as attr」の現在の状態はどうなのだろうと思いました。いくつかのコメンターが指摘しているように、いくつかの落とし穴やフットガンがあり、その中には非常に微妙なものもあるので、これはおそらく自分でゼロから始めたいものでないでしょう。また、私はNamespace基本クラスとして使用することをお勧めしません、私はその道を進んでいます、それはきれいではありません。

幸い、この機能を提供するいくつかのオープンソースパッケージがあり、pipインストールの準備ができています!残念ながら、いくつかのパッケージがあります。2019年12月現在の概要は次のとおりです。

候補者(マスターへの最近のコミット| #commits | #contribs | coverage%):

  • 中毒者 (2019-04-28 | 217 | 22 | 100%)
  • ムンク(2019-12-16 | 160 | 17 |?%)
  • easydict(2018-10-18 | 51 | 6 |?%)
  • attrdict(2019-02-01 | 108 | 5 | 100%)
  • 予測 (2019-10-01 | 65 | 1 |?%)

保守または保守不足:

  • treedict(2014-03-28 | 95 | 2 |?%)
  • (2012-03-12 | 20 | 2 |?%)
  • NeoBunch

私は現在ムンク中毒者をお勧めします。コミット、コントリビューター、リリースが最も多く、それぞれの健全なオープンソースコードベースを示唆しています。それらは、最もきれいに見えるreadme.md、100%のカバレッジ、そして見栄えの良い一連のテストを備えています。

私はこれらのオプションすべてに気付いていなかったので、自分のdict / attrコードを転がして大量の時間を無駄にした以外に、このレースには犬がいません(今のところ!)。断片化されたパッケージの束ではなく、1つの堅固なパッケージを見たいので、私は将来、常習者/大量生産に貢献するかもしれません。それらが好きなら、貢献してください!特に、ムンクはcodecovバッジを使用でき、アディクトはpythonバージョンのバッジを使用できるように見えます。

中毒プロ:

  • 再帰的な初期化(foo.abc = 'bar')、dictのような引数が中毒になります。Dict

常習者の短所:

  • typing.Dictあなたが影ならfrom addict import Dict
  • キーチェックなし。再帰的な初期化を許可しているため、キーのスペルを間違えた場合、KeyErrorではなく新しい属性を作成するだけです(AljoStに感謝)。

ムンクプロ:

  • ユニークなネーミング
  • JSONおよびYAML用の組み込みのser / de関数

短所:

  • 再帰的な初期化なし/一度に1つの属性しか初期化できません

ここで私は編集します

何ヶ月も前に、テキストエディタを使用してpythonを作成したとき、自分または他の1人の開発者だけのプロジェクトでは、dict-attrsのスタイル、つまりを宣言するだけでキーを挿入できる機能が気に入りましたfoo.bar.spam = eggs。現在はチームで作業していて、すべてにIDEを使用しています。静的分析、機能的手法、およびタイプヒントを優先して、これらの種類のデータ構造および動的型付けから離れていきました。私はこのテクニックの実験を始め、自分のデザインのオブジェクトでPstructをサブクラス化しました:

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))


class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

これにより、ディクショナリのように動作するオブジェクトが得られますが、属性などのキーにはるかに厳密な方法でアクセスすることもできます。ここでの利点は、私(またはあなたのコードの不運なコンシューマー)が存在できるフィールドと存在できないフィールドを正確に知っていること、そしてIDEがフィールドをオートコンプリートできることです。また、バニラdictをサブクラス化すると、jsonのシリアル化が簡単になります。このアイデアの次の進化は、これらのインターフェースを生成するカスタムprotobufジェネレーターであると思います。優れたノックオンは、gRPCを介してクロス言語のデータ構造とIPCをほぼ無料で取得できることです。

attr-dictsを使用する場合は、自分(およびチームメイト)の健全性のために、どのフィールドが期待されるかを文書化することが不可欠です。

この投稿を最新に保つために、この投稿を自由に編集/更新してください。


2
大きな欠点はaddict、新しいスペルを返すため、属性のスペルを間違えても例外が発生しないことですDict(これはfoo.abc = 'bar'が機能するために必要です)。
AljoSt

5

組み込みを使用した不変のレコードの短い例を次に示しcollections.namedtupleます。

def record(name, d):
    return namedtuple(name, d.keys())(**d)

そして使用例:

rec = record('Model', {
    'train_op': train_op,
    'loss': loss,
})

print rec.loss(..)

5

回答にいくつかの種類を追加するために、sci-kit learnはこれをとして実装していBunchます:

class Bunch(dict):                                                              
    """ Scikit Learn's container object                                         

    Dictionary-like object that exposes its keys as attributes.                 
    >>> b = Bunch(a=1, b=2)                                                     
    >>> b['b']                                                                  
    2                                                                           
    >>> b.b                                                                     
    2                                                                           
    >>> b.c = 6                                                                 
    >>> b['c']                                                                  
    6                                                                           
    """                                                                         

    def __init__(self, **kwargs):                                               
        super(Bunch, self).__init__(kwargs)                                     

    def __setattr__(self, key, value):                                          
        self[key] = value                                                       

    def __dir__(self):                                                          
        return self.keys()                                                      

    def __getattr__(self, key):                                                 
        try:                                                                    
            return self[key]                                                    
        except KeyError:                                                        
            raise AttributeError(key)                                           

    def __setstate__(self, state):                                              
        pass                       

必要なのはsetattrおよびgetattrメソッドを取得することだけですgetattr。dictキーのチェックと実際の属性のチェックに移ります。これsetstaetは、「房」のピクルス化/ピクルル化を修正するための修正です-ネストされている場合は、https: //github.com/scikit-learn/scikit-learn/issues/6196を確認してください


3

setattr()とgetattr()はすでに存在しているので、独自に記述する必要はありません 。

クラスオブジェクトの利点は、おそらくクラスの定義と継承に関係しています。


3

このスレッドからの入力に基づいてこれを作成しました。ただし、odictを使用する必要があるため、getおよびset attrをオーバーライドする必要がありました。これは、特別な用途の大部分で機能するはずです。

使用法は次のようになります。

# Create an ordered dict normally...
>>> od = OrderedAttrDict()
>>> od["a"] = 1
>>> od["b"] = 2
>>> od
OrderedAttrDict([('a', 1), ('b', 2)])

# Get and set data using attribute access...
>>> od.a
1
>>> od.b = 20
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])

# Setting a NEW attribute only creates it on the instance, not the dict...
>>> od.c = 8
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])
>>> od.c
8

クラス:

class OrderedAttrDict(odict.OrderedDict):
    """
    Constructs an odict.OrderedDict with attribute access to data.

    Setting a NEW attribute only creates it on the instance, not the dict.
    Setting an attribute that is a key in the data will set the dict data but 
    will not create a new instance attribute
    """
    def __getattr__(self, attr):
        """
        Try to get the data. If attr is not a key, fall-back and get the attr
        """
        if self.has_key(attr):
            return super(OrderedAttrDict, self).__getitem__(attr)
        else:
            return super(OrderedAttrDict, self).__getattr__(attr)


    def __setattr__(self, attr, value):
        """
        Try to set the data. If attr is not a key, fall-back and set the attr
        """
        if self.has_key(attr):
            super(OrderedAttrDict, self).__setitem__(attr, value)
        else:
            super(OrderedAttrDict, self).__setattr__(attr, value)

これはすでにスレッドで言及されているかなりクールなパターンですが、dictを取得して、それをIDEのオートコンプリートで機能するオブジェクトに変換したい場合などです。

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d


3

これは私が使うものです

args = {
        'batch_size': 32,
        'workers': 4,
        'train_dir': 'train',
        'val_dir': 'val',
        'lr': 1e-3,
        'momentum': 0.9,
        'weight_decay': 1e-4
    }
args = namedtuple('Args', ' '.join(list(args.keys())))(**args)

print (args.lr)

これは素早い、汚い答えです。私の唯一の観察/コメントはあなたのソリューションを簡素化することができるので、私は(私が思うに)、namedtupleコンストラクタは、文字列のリストを受け入れると思うことです:namedtuple('Args', list(args.keys()))(**args)
ダン・グエン

2

私が作ったばかりのこのクラスを使用してそれを行うことができます。このクラスを使用すると、Mapオブジェクトを別の辞書(jsonシリアル化を含む)のように、またはドット表記で使用できます。お役に立てれば幸いです。

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

使用例:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

1
それは影ができることを注記dictする方法を、例えば:m=Map(); m["keys"] = 42; m.keys()与えますTypeError: 'int' object is not callable
bfontaine

@bfontaineアイデアはのようなものでfield/attributeありmethod、メソッドではなく番号を割り当てる場合は、でそのメソッドにアクセスできますm.method()
16

2

Kinvaisの回答に基づいて、http: //databio.org/posts/python_AttributeDict.htmlで提案されているAttributeDictのアイデアを統合した別の実装を投稿しましょう。

このバージョンの利点は、ネストされた辞書でも機能することです。

class AttrDict(dict):
    """
    A class to convert a nested Dictionary into an object with key-values
    that are accessible using attribute notation (AttrDict.attribute) instead of
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
    """

    # Inspired by:
    # http://stackoverflow.com/a/14620633/1551810
    # http://databio.org/posts/python_AttributeDict.html

    def __init__(self, iterable, **kwargs):
        super(AttrDict, self).__init__(iterable, **kwargs)
        for key, value in iterable.items():
            if isinstance(value, dict):
                self.__dict__[key] = AttrDict(value)
            else:
                self.__dict__[key] = value

1
class AttrDict(dict):

     def __init__(self):
           self.__dict__ = self

if __name__ == '____main__':

     d = AttrDict()
     d['ray'] = 'hope'
     d.sun = 'shine'  >>> Now we can use this . notation
     print d['ray']
     print d.sun

1

解決策は次のとおりです。

DICT_RESERVED_KEYS = vars(dict).keys()


class SmartDict(dict):
    """
    A Dict which is accessible via attribute dot notation
    """
    def __init__(self, *args, **kwargs):
        """
        :param args: multiple dicts ({}, {}, ..)
        :param kwargs: arbitrary keys='value'

        If ``keyerror=False`` is passed then not found attributes will
        always return None.
        """
        super(SmartDict, self).__init__()
        self['__keyerror'] = kwargs.pop('keyerror', True)
        [self.update(arg) for arg in args if isinstance(arg, dict)]
        self.update(kwargs)

    def __getattr__(self, attr):
        if attr not in DICT_RESERVED_KEYS:
            if self['__keyerror']:
                return self[attr]
            else:
                return self.get(attr)
        return getattr(self, attr)

    def __setattr__(self, key, value):
        if key in DICT_RESERVED_KEYS:
            raise AttributeError("You cannot set a reserved name as attribute")
        self.__setitem__(key, value)

    def __copy__(self):
        return self.__class__(self)

    def copy(self):
        return self.__copy__()

1

この方法でdictキーにアクセスする際の注意点と落とし穴は何でしょうか?

@Henryが示唆するように、ドットアクセスがdictsで使用されない可能性がある1つの理由は、dictキー名をpython-valid変数に制限し、それによってすべての可能な名前を制限することです。

以下は、dictが与えられている場合に、ドットで区切られたアクセスが一般的に役に立たない理由の例dです。

有効

次の属性はPythonでは無効です。

d.1_foo                           # enumerated names
d./bar                            # path names
d.21.7, d.12:30                   # decimals, time
d.""                              # empty strings
d.john doe, d.denny's             # spaces, misc punctuation 
d.3 * x                           # expressions  

スタイル

PEP8の規則は、属性の命名に弱い制約を課します。

A.予約済みキーワード(または組み込み関数)の名前:

d.in
d.False, d.True
d.max, d.min
d.sum
d.id

関数の引数の名前が予約済みのキーワードと競合する場合は、通常、末尾に下線を1つ追加する方が適切です...

B. メソッド変数名のケースルール:

変数名は、関数名と同じ規則に従います。

d.Firstname
d.Country

関数の命名規則を使用します。読みやすくするために、必要に応じて単語をアンダースコアで区切った小文字にします。


時々これらの懸念はパンダのような図書館で提起されます pandasの、DataFrameカラムに名前でドットでアクセスできます。命名制限を解決するデフォルトのメカニズムは、配列表記(角括弧内の文字列)でもあります。

これらの制約がユースケースに当てはまらない場合、ドットアクセスデータ構造にはいくつかのオプションがあります。


1

あなたはdict_to_obj使用することができます https://pypi.org/project/dict-to-obj/ それはあなたが尋ねたまさにん

From dict_to_obj import DictToObj
a = {
'foo': True
}
b = DictToObj(a)
b.foo
True

1
これは.idea、ユーザー固有のファイルまたはIDEで生成されたファイルをに配置するのに適した形式.gitignoreです。
DeusXMachina

1

これは「良い」答えではありませんが、私はこれは気の利いたものだと思いました(現在の形式でネストされたdictを処理しません)。dictを関数でラップするだけです:

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

これで、構文が少し異なります。属性としてdict項目にアクセスするには、次のようにしますf.key。通常の方法でdict項目(および他のdictメソッド)にアクセスするには、次のようにします。f()['key']キーワード引数または辞書、あるいはその両方を指定してfを呼び出すことにより、dictを簡単に更新できます。

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

そして、それがあります。誰かがこの方法の利点と欠点を提案したら私は幸せです。


0

Dougが述べたように、obj.key機能を実現するために使用できるBunchパッケージがあります。実際、新しいバージョンと呼ばれています

NeoBunch

ただし、neobunchify関数を使用して、dictをNeoBunchオブジェクトに変換する優れた機能があります。私はMakoテンプレートを頻繁に使用し、NeoBunchオブジェクトがそれらをはるかに読みやすくするようにデータを渡すので、Pythonプログラムで通常のdictを使用することになったが、Makoテンプレートでドット表記が必要な場合は、そのように使用できます。

from mako.template import Template
from neobunch import neobunchify

mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
    out_file.write(mako_template.render(**neobunchify(data)))

そして、Makoテンプレートは次のようになります。

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor

NeoBunchへのリンクは404
DeusXMachina

0

最も簡単な方法は、クラスを定義して名前空間と呼びましょう。これは、dictでオブジェクトdict .update()を使用します。その後、dictはオブジェクトとして扱われます。

class Namespace(object):
    '''
    helps referencing object in a dictionary as dict.key instead of dict['key']
    '''
    def __init__(self, adict):
        self.__dict__.update(adict)



Person = Namespace({'name': 'ahmed',
                     'age': 30}) #--> added for edge_cls


print(Person.name)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.