我如何使Python字典成员访问通过点“。”?
例如,我想写mydict.val而不是mydict['val']。
我还想以这种方式访问嵌套字典。例如
mydict.mydict2.val
会提到
mydict = { 'mydict2': { 'val': ... } }
我如何使Python字典成员访问通过点“。”?
例如,我想写mydict.val而不是mydict['val']。
我还想以这种方式访问嵌套字典。例如
mydict.mydict2.val
会提到
mydict = { 'mydict2': { 'val': ... } }
当前回答
这是我从很久以前的一个项目里挖出来的。它可能还可以再优化一点,但就是这样了。
class DotNotation(dict):
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
def __init__(self, data):
if isinstance(data, str):
data = json.loads(data)
for name, value in data.items():
setattr(self, name, self._wrap(value))
def __getattr__(self, attr):
def _traverse(obj, attr):
if self._is_indexable(obj):
try:
return obj[int(attr)]
except:
return None
elif isinstance(obj, dict):
return obj.get(attr, None)
else:
return attr
if '.' in attr:
return reduce(_traverse, attr.split('.'), self)
return self.get(attr, None)
def _wrap(self, value):
if self._is_indexable(value):
# (!) recursive (!)
return type(value)([self._wrap(v) for v in value])
elif isinstance(value, dict):
return DotNotation(value)
else:
return value
@staticmethod
def _is_indexable(obj):
return isinstance(obj, (tuple, list, set, frozenset))
if __name__ == "__main__":
test_dict = {
"dimensions": {
"length": "112",
"width": "103",
"height": "42"
},
"meta_data": [
{
"id": 11089769,
"key": "imported_gallery_files",
"value": [
"https://example.com/wp-content/uploads/2019/09/unnamed-3.jpg",
"https://example.com/wp-content/uploads/2019/09/unnamed-2.jpg",
"https://example.com/wp-content/uploads/2019/09/unnamed-4.jpg"
]
}
]
}
dotted_dict = DotNotation(test_dict)
print(dotted_dict.dimensions.length) # => '112'
print(getattr(dotted_dict, 'dimensions.length')) # => '112'
print(dotted_dict.meta_data[0].key) # => 'imported_gallery_files'
print(getattr(dotted_dict, 'meta_data.0.key')) # => 'imported_gallery_files'
print(dotted_dict.meta_data[0].value) # => ['link1','link2','link2']
print(getattr(dotted_dict, 'meta_data.0.value')) # => ['link1','link2','link3']
print(dotted_dict.meta_data[0].value[2]) # => 'link3'
print(getattr(dotted_dict, 'meta_data.0.value.2')) # => 'link3'
其他回答
我的观点:出于我自己的目的,我开发了minydra,一个简单的命令行解析器,包括一个自定义类MinyDict(灵感来自addict):
In [1]: from minydra import MinyDict
In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).pretty_print(); args
╭──────────────────────────────╮
│ foo : bar │
│ yes.no.maybe : idontknow │
╰──────────────────────────────╯
Out[2]: {'foo': 'bar', 'yes.no.maybe': 'idontknow'}
In [3]: args.resolve().pretty_print(); args
╭──────────────────────────╮
│ foo : bar │
│ yes │
│ │no │
│ │ │maybe : idontknow │
╰──────────────────────────╯
Out[3]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}
In [4]: args.yes.no.maybe
Out[4]: "idontknow"
In [5]: "foo" in args
Out[5]: True
In [6]: "rick" in args
Out[6]: False
In [7]: args.morty is None
Out[7]: True
In [8]: args.items()
Out[8]: dict_items([('foo', 'bar'), ('yes', {'no': {'maybe': 'idontknow'}})])
它通过向json yaml和pickle添加转储/加载方法来上瘾,并且在MinyDict.update()中也有一个严格的模式来防止创建新键(这对于防止命令行中的错字很有用)
派生自dict和并实现__getattr__和__setattr__。
或者你也可以用Bunch,非常相似。
我不认为这是可能的monkeypatch内置字典类。
此解决方案是对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.
我只需要使用虚线路径字符串访问字典,所以我想到了:
def get_value_from_path(dictionary, parts):
""" extracts a value from a dictionary using a dotted path string """
if type(parts) is str:
parts = parts.split('.')
if len(parts) > 1:
return get_value_from_path(dictionary[parts[0]], parts[1:])
return dictionary[parts[0]]
a = {'a':{'b':'c'}}
print(get_value_from_path(a, 'a.b')) # c
如果你已经在使用pandas,你可以构造一个pandas Series或DataFrame,从中你可以通过点语法访问项目:
1级字典:
import pandas as pd
my_dictionary = pd.Series({
'key1': 'value1',
'key2': 'value2'
})
print(my_dictionary.key1)
# Output: value1
2级字典:
import pandas as pd
my_dictionary = pd.DataFrame({
'key1': {
'inner_key1': 'value1'
},
'key2': {
'inner_key2': 'value2'
}
})
print(my_dictionary.key1.inner_key1)
# Output: value1
请注意,这可能在规范化数据结构(其中每个字典条目都具有相同的结构)下工作得更好。在上面的第二个例子中,得到的DataFrame是:
key1 key2
inner_key1 value1 NaN
inner_key2 NaN value2