我如何使Python字典成员访问通过点“。”?
例如,我想写mydict.val而不是mydict['val']。
我还想以这种方式访问嵌套字典。例如
mydict.mydict2.val
会提到
mydict = { 'mydict2': { 'val': ... } }
我如何使Python字典成员访问通过点“。”?
例如,我想写mydict.val而不是mydict['val']。
我还想以这种方式访问嵌套字典。例如
mydict.mydict2.val
会提到
mydict = { 'mydict2': { 'val': ... } }
当前回答
这是一个老问题,但我最近发现sklearn有一个可通过键访问的实现版本字典,即Bunch https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html#sklearn.utils.Bunch
其他回答
可以使用dotsi来支持完整列表、dict和递归,并使用一些扩展方法
pip install dotsi
and
>>> import dotsi
>>>
>>> d = dotsi.Dict({"foo": {"bar": "baz"}}) # Basic
>>> d.foo.bar
'baz'
>>> d.users = [{"id": 0, "name": "Alice"}] # List
>>> d.users[0].name
'Alice'
>>> d.users.append({"id": 1, "name": "Becca"}); # Append
>>> d.users[1].name
'Becca'
>>> d.users += [{"id": 2, "name": "Cathy"}]; # `+=`
>>> d.users[2].name
'Cathy'
>>> d.update({"tasks": [{"id": "a", "text": "Task A"}]});
>>> d.tasks[0].text
'Task A'
>>> d.tasks[0].tags = ["red", "white", "blue"];
>>> d.tasks[0].tags[2];
'blue'
>>> d.tasks[0].pop("tags") # `.pop()`
['red', 'white', 'blue']
>>>
>>> import pprint
>>> pprint.pprint(d)
{'foo': {'bar': 'baz'},
'tasks': [{'id': 'a', 'text': 'Task A'}],
'users': [{'id': 0, 'name': 'Alice'},
{'id': 1, 'name': 'Becca'},
{'id': 2, 'name': 'Cathy'}]}
>>>
>>> type(d.users) # dotsi.Dict (AKA dotsi.DotsiDict)
<class 'dotsi.DotsiList'>
>>> type(d.users[0]) # dotsi.List (AKA dotsi.DotsiList)
<class 'dotsi.DotsiDict'>
>>>
我最近遇到了“Box”库,它也做同样的事情。
安装命令:pip install python-box
例子:
from box import Box
mydict = {"key1":{"v1":0.375,
"v2":0.625},
"key2":0.125,
}
mydict = Box(mydict)
print(mydict.key1.v1)
我发现它比其他现有的库(如dotmap)更有效,当你有大量嵌套字典时,dotmap会产生python递归错误。
链接到图书馆和详细信息:https://pypi.org/project/python-box/
通过pip安装dotmap
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
这意味着如果你想访问的东西已经是字典形式的,你可以把它转换成DotMap来方便访问:
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city
最后,它会自动创建新的子DotMap实例,你可以这样做:
m = DotMap()
m.people.steve.age = 31
与Bunch的比较
完全公开:我是DotMap的创造者。我创建它是因为Bunch缺少这些功能
记住添加的顺序项并按此顺序迭代 自动创建子DotMap,当你有很多层次结构时,这节省了时间,并使代码更干净 从字典构造并递归地将所有子字典实例转换为DotMap
这也适用于嵌套字典,并确保后面追加的字典行为相同:
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__
这是我对@derek73的回答。我用字典。__getitem__作为__getattr__,因此它仍然抛出KeyError,并且im重命名字典公共方法以“”前缀(“”包围导致特殊方法名称冲突,如__get__将被视为一个描述符方法)。无论如何,由于关键的dict基方法,您无法将键作为属性获得完全清晰的命名空间,因此解决方案并不完美,但您可以拥有键属性,如get, pop, items等。
class DotDictMeta(type):
def __new__(
cls,
name,
bases,
attrs,
rename_method=lambda n: f'__{n}__',
**custom_methods,
):
d = dict
attrs.update(
cls.get_hidden_or_renamed_methods(rename_method),
__getattr__=d.__getitem__,
__setattr__=d.__setitem__,
__delattr__=d.__delitem__,
**custom_methods,
)
return super().__new__(cls, name, bases, attrs)
def __init__(self, name, bases, attrs, **_):
super().__init__(name, bases, attrs)
@property
def attribute_error(self):
raise AttributeError
@classmethod
def get_hidden_or_renamed_methods(cls, rename_method=None):
public_methods = tuple(
i for i in dict.__dict__.items() if not i[0].startswith('__')
)
error = cls.attribute_error
hidden_methods = ((k, error) for k, v in public_methods)
yield from hidden_methods
if rename_method:
renamed_methods = ((rename_method(k), v) for k, v in public_methods)
yield from renamed_methods
class DotDict(dict, metaclass=DotDictMeta):
pass
你可以从DotDict命名空间中删除dict方法,并继续使用dict类方法,当你想操作其他dict实例并希望使用相同的方法而不需要额外检查它是否为DotDict时,它也很有用。
dct = dict(a=1)
dot_dct = DotDict(b=2)
foo = {c: i for i, c in enumerate('xyz')}
for d in (dct, dot_dct):
# you would have to use dct.update and dot_dct.__update methods
dict.update(d, foo)
assert dict.get(dot, 'foo', 0) is 0