ドット「。」を介してPython辞書メンバーにアクセスできるようにするにはどうすればよいですか?
たとえば、mydict['val']
と書く代わりに、mydict.val
と書きたいと思います。
また、この方法でネストされた辞書にアクセスしたいと思います。例えば
mydict.mydict2.val
を参照します
mydict = { 'mydict2': { 'val': ... } }
作成したばかりのこのクラスを使用して実行できます。このクラスを使用すると、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']
私はこれを常に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'
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
に再帰的に変換しますDictから派生し、__getattr__
および__setattr__
を実装します。
または、非常によく似た Bunch を使用できます。
組み込みのdictクラスをモンキーパッチすることは不可能だと思います。
私はこれを試しました:
class dotdict(dict):
def __getattr__(self, name):
return self[name]
__getattribute__
も試すことができます。
すべてのdictをdotdictのタイプで十分にします。これをマルチレイヤーdictから初期化する場合は、__init__
も実装してください。
ファブリック には本当にすてきな、最小限の 実装 があります。ネストされたアクセスを可能にするためにそれを拡張すると、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の答えを少し詳しく説明します。
変更した辞書をピクルス化する場合は、上記の回答にいくつかの状態メソッドを追加する必要があります。
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
しないでください。 Pythonでは、属性アクセスとインデックス作成は別個のものであり、同じことを実行したくないはずです。アクセス可能な属性が必要なものがある場合はクラス(おそらくnamedtuple
で作成されたクラス)を作成し、[]
表記を使用してdictからアイテムを取得します。
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
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
Munch が好きで、ドットアクセスに加えて多くの便利なオプションを提供します。
輸入ムンク
temp_1 = {'person':{'fname': 'senthil'、 'lname': 'ramalingam'}}
dict_munch = munch.munchify(temp_1)
dict_munch.person.fname
言語自体はこれをサポートしていませんが、時々これはまだ有用な要件です。 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'
>>
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]
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)
私は AttrDict と Bunch の両方のライブラリを試してみましたが、それらは私の使用を遅らせる方法であることがわかりました。友人と私が調べたところ、これらのライブラリを記述する主な方法は、ライブラリがネストされたオブジェクトを積極的に再帰し、辞書オブジェクトのコピーを作成することを発見しました。これを念頭に置いて、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つは、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"}
私は自分の解決策をリングに投げ入れたい:
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__
このソリューションは、 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
のようなことができます。
デリケートなソリューションの種類
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))
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"