web-dev-qa-db-ja.com

ドット「。」の使用方法辞書のメンバーにアクセスするには?

ドット「。」を介してPython辞書メンバーにアクセスできるようにするにはどうすればよいですか?

たとえば、mydict['val']と書く代わりに、mydict.valと書きたいと思います。

また、この方法でネストされた辞書にアクセスしたいと思います。例えば

mydict.mydict2.val 

を参照します

mydict = { 'mydict2': { 'val': ... } }
214
bodacydo

作成したばかりのこのクラスを使用して実行できます。このクラスを使用すると、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!'
# Or
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']
125
epool

私はこれを常にutilファイルに保管しています。独自のクラスのミックスインとしても使用できます。

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'
197
derek73

dotmap経由でpipをインストールします

pip install dotmap

やりたいことはすべて行い、dictをサブクラス化するため、通常の辞書のように動作します。

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

さらに、dictオブジェクトとの間で変換できます。

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

これは、アクセスしたいものが既にdict形式になっている場合、簡単にアクセスできるようにDotMapに変換できることを意味します。

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

最後に、新しい子DotMapインスタンスが自動的に作成されるため、次のようなことができます。

m = DotMap()
m.people.steve.age = 31

バンチとの比較

完全な開示: DotMap の作成者です。 Bunchにこれらの機能がないため、作成しました

  • 注文アイテムが追加されたことを覚えて、その順序で繰り返します
  • 子を自動的にDotMap作成します。これにより、時間を節約し、多くの階層がある場合にコードがきれいになります。
  • dictから構築し、すべての子dictインスタンスをDotMapに再帰的に変換します
95
Chris Redford

Dictから派生し、__getattr__および__setattr__を実装します。

または、非常によく似た Bunch を使用できます。

組み込みのdictクラスをモンキーパッチすることは不可能だと思います。

56
Kugel

私はこれを試しました:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

__getattribute__も試すことができます。

すべてのdictをdotdictのタイプで十分にします。これをマルチレイヤーdictから初期化する場合は、__init__も実装してください。

18
tdihp

ファブリック には本当にすてきな、最小限の 実装 があります。ネストされたアクセスを可能にするためにそれを拡張すると、defaultdictを使用でき、結果は次のようになります。

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

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

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

次のように使用します。

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

"dictから派生して実装し、__getattr__および__setattr__"のKugelの答えを少し詳しく説明します。

15
Dave

変更した辞書をピクルス化する場合は、上記の回答にいくつかの状態メソッドを追加する必要があります。

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self
8
volodymyr

しないでください。 Pythonでは、属性アクセスとインデックス作成は別個のものであり、同じことを実行したくないはずです。アクセス可能な属性が必要なものがある場合はクラス(おそらくnamedtupleで作成されたクラス)を作成し、[]表記を使用してdictからアイテムを取得します。

7
Mike Graham

Kugelの答えに基づいて、Mike Grahamの注意の言葉を考慮に入れて、ラッパーを作成したらどうなるでしょうか?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setattr__(self, name, value):
    self._data[name] = value  

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other
6
M J

Python 3.4.3で動作する非常にシンプルな__getattr__を使用します

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

出力:

10000
StackOverflow
5
IRSHAD

Munch が好きで、ドットアクセスに加えて多くの便利なオプションを提供します。

輸入ムンク

temp_1 = {'person':{'fname': 'senthil'、 'lname': 'ramalingam'}}

dict_munch = munch.munchify(temp_1)

dict_munch.person.fname

5
Senthil

言語自体はこれをサポートしていませんが、時々これはまだ有用な要件です。 Bunchレシピの他に、ドット付き文字列を使用して辞書にアクセスできる小さなメソッドを作成することもできます。

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

これは次のようなものをサポートします:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
4
pbanka

Epoolの答えに基づいて構築するために、このバージョンでは、ドット演算子を使用して内部のdictにアクセスできます。

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

たとえば、foo.bar.baz[1].baba"loo"を返します。

class Map(dict):
    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():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                Elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            Elif isinstance(v[elem], list):
                self.__convert(v[elem])

    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]
3
touch my body
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

dictをオブジェクトに永続的に変換することに決めた場合、これを行う必要があります。アクセスする直前に使い捨てオブジェクトを作成できます。

d = dict_to_object(d)
3
nehemiah

私は AttrDictBunch の両方のライブラリを試してみましたが、それらは私の使用を遅らせる方法であることがわかりました。友人と私が調べたところ、これらのライブラリを記述する主な方法は、ライブラリがネストされたオブジェクトを積極的に再帰し、辞書オブジェクトのコピーを作成することを発見しました。これを念頭に置いて、2つの重要な変更を行いました。 1)遅延ロードされた属性を作成しました2)辞書オブジェクトのコピーを作成する代わりに、軽量プロキシオブジェクトのコピーを作成します。これが最終的な実装です。このコードを使用すると、パフォーマンスが大幅に向上します。 AttrDictまたはBunchを使用する場合、これらの2つのライブラリだけで、リクエスト時間のそれぞれ1/2と1/3を消費しました(what !?)。このコードは、その時間をほとんどゼロ(0.5msの範囲内)に短縮しました。もちろん、これはニーズに依存しますが、コードでこの機能をかなり使用している場合は、このような単純なものを使用してください。

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

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

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

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

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

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (Tuple, list)):
        return ListProxy(value)
    return value

元の実装を参照してください here by https://stackoverflow.com/users/704327/michael-merickel .

もう1つ注意すべき点は、この実装は非常に単純であり、必要なすべてのメソッドを実装していないことです。必要に応じて、DictProxyまたはListProxyオブジェクトにこれらを記述する必要があります。

1
JayD3e

ドットアクセス(配列アクセスではない)を取得する簡単な方法の1つは、Pythonでプレーンオブジェクトを使用することです。このような:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

...そして次のように使用します:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

... dictに変換するには:

>>> print(obj.__dict__)
{"key": "value"}
0
Emil Stenström

私は自分の解決策をリングに投げ入れたい:

https://github.com/skorokithakis/jsane

JSONを解析してwith.attribute.lookups.like.this.r()にアクセスできるものにすることができます。これは主に、作業を開始する前にこの回答を見ていなかったためです。

これはネストされた辞書でも機能し、後で追加される辞書が同じように動作することを確認します。

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__
0
Yaniv K.

このソリューションは、 epool が提供するものを改良したもので、OPがネストされた辞書に一貫した方法でアクセスするという要件に対処します。 epoolによる解決策では、ネストされた辞書にアクセスできませんでした。

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


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

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

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

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

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

このクラスを使用すると、A.B.C.Dのようなことができます。

0
deepak

デリケートなソリューションの種類

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))
0
Yonks Somarl

OPの質問に対する直接的な答えではありませんが、一部の人に触発され、おそらく役に立つでしょう。内部__dict__(決して最適化されたコードではない)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

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

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"
0