[英]How to convert a nested python dictionary into a simple namespace?
假设我有一个深度为 N 的嵌套字典。如何将每个内部嵌套字典转换为一个简单的命名空间?
example_input = {key0a: "test", key0b: {key1a: {key2a: {...keyNx}, key2b: "test"} ,key1b: "test"}}
example_output = SimpleNamespace(key0a: "test", key0b: SimpleNamespace(key1a: SimpleNamespace(key2a: SimpleNamespace(...keyNx), key2b: "test"), key1b: "test"))
如果给出了 example_input 字典,是否有更好的替代方法可以使字典的键可以按点表示法访问(例如 example_input.key0a) - 没有外部依赖项?
只是回答你的第二个(最后一个)问题 - 这是一个热门话题,存在许多不同的项目(非标准),可以让你的字典变成点符号 object。
例如这个 one- attrdict 。 通过pip install attrdict
安装它。
使用示例:
from attrdict import AttrDict
d = {'a': 1, 'b': [{'c': 2}, {'d': {'e': {'f': {5: {'g': 3}}}}}]}
ad = AttrDict(d)
print(ad.b[1].d.e.f(5).g) # 3
如果你想知道attrdict 之类的模块是如何实现的,那么我写了一个非常简单的类似功能的实现(当然真正的attrdict
应该更丰富):
class AttrD(object):
def __init__(self, d = {}):
self.set_d(d)
def __getattr__(self, key):
return AttrD(self.get_or_create(key))
def __setattr__(self, key, value):
self.set_or_create(key, value)
def __getitem__(self, key):
return AttrD(self.get_or_create(key))
def __setitem__(self, key, value):
self.set_or_create(key, value)
def __call__(self, key):
return AttrD(self.get_or_create(key))
def __repr__(self):
return repr(self._d)
def to_obj(self):
return self._d
def set_d(self, d):
super(AttrD, self).__setattr__('_d', d)
def get_or_create(self, name):
if type(self._d) in (dict,) and name not in self._d:
self._d[name] = {}
if type(self._d) in (list, tuple) and len(self._d) <= name:
self.set_d(self._d + type(self._d)(
[None] * (name + 1 - len(self._d))))
return self._d[name]
def set_or_create(self, key, value):
self.get_or_create(key)
self._d[key] = value
ad = AttrD({'a': 1, 'b': [{'c': 2}, {'d': {'e': {'f': {5: {'g': 3}}}}}]})
ad.b[1].d.e.f(5).g = [4, 5, 6]
print(ad.b[1].d.e.f(5).g[2]) # 6
print(AttrD({'a': 123}).b.c) # Non-existent defaults to {}
example_input = {'key0a': "test", 'key0b':
{'key1a': {'key2a': 'end', 'key2b': "test"} ,'key1b': "test"},
"something": "else"}
def parse(d):
x = SimpleNamespace()
_ = [setattr(x, k, parse(v)) if isinstance(v, dict) else setattr(x, k, v) for k, v in d.items() ]
return x
result = parse(example_input)
print (result)
Output:
namespace(key0a='test',
key0b=namespace(key1a=namespace(key2a='end', key2b='test'), key1b='test'),
something='else')
2022 回答:现在我发布了一个很小、相对较快的库,称为dotwiz
,它也可用于为 python dict
object 提供简单的点访问。
巧合的是,它应该比其他选项快一点——我添加了一个快速而肮脏的基准代码,我使用下面的timeit
模块放在一起,针对attrdict
和SimpleNamespace
方法计时——后者实际上执行有时相当稳固。
请注意,例如,我必须稍微修改
parse
function,以便它处理list
object 中的嵌套dict
。
from timeit import timeit
from types import SimpleNamespace
from attrdict import AttrDict
from dotwiz import DotWiz
example_input = {'key0a': "test", 'key0b': {'key1a': [{'key2a': 'end', 'key2b': "test"}], 'key1b': "test"},
"something": "else"}
def parse(d):
x = SimpleNamespace()
_ = [setattr(x, k,
parse(v) if isinstance(v, dict)
else [parse(e) for e in v] if isinstance(v, list)
else v) for k, v in d.items()]
return x
print('-- Create')
print('attrdict: ', round(timeit('AttrDict(example_input)', globals=globals()), 2))
print('dotwiz: ', round(timeit('DotWiz(example_input)', globals=globals()), 2))
print('SimpleNamespace: ', round(timeit('parse(example_input)', globals=globals()), 2))
print()
dw = DotWiz(example_input)
ns = parse(example_input)
ad = AttrDict(example_input)
print('-- Get')
print('attrdict: ', round(timeit('ad.key0b.key1a[0].key2a', globals=globals()), 2))
print('dotwiz: ', round(timeit('dw.key0b.key1a[0].key2a', globals=globals()), 2))
print('SimpleNamespace: ', round(timeit('ns.key0b.key1a[0].key2a', globals=globals()), 2))
print()
print(ad)
print(dw)
print(ns)
assert ad.key0b.key1a[0].key2a \
== dw.key0b.key1a[0].key2a \
== ns.key0b.key1a[0].key2a \
== 'end'
以下是我的 M1 Mac Pro 笔记本电脑上的结果:
attrdict: 0.69
dotwiz: 1.3
SimpleNamespace: 1.38
-- Get
attrdict: 6.06
dotwiz: 0.06
SimpleNamespace: 0.06
dotwiz库可以与pip
一起安装:
$ pip install dotwiz
基于 mujjija 的解决方案,这就是我想出的。 完整代码如下
from types import SimpleNamespace
def parse(data):
if type(data) is list:
return list(map(parse, data))
elif type(data) is dict:
sns = SimpleNamespace()
for key, value in data.items():
setattr(sns, key, parse(value))
return sns
else:
return data
info = {
'country': 'Australia',
'number': 1,
'slangs': [
'no worries mate',
'winner winner chicken dinner',
{
'no_slangs': [123, {'definately_not': 'hello'}]
}
],
'tradie': {
'name': 'Rizza',
'occupation': 'sparkie'
}
}
d = parse(info)
assert d.country == 'Australia'
assert d.number == 1
assert d.slangs[0] == 'no worries mate'
assert d.slangs[1] == 'winner winner chicken dinner'
assert d.slangs[2].no_slangs[0] == 123
assert d.slangs[2].no_slangs[1].definately_not == 'hello'
assert d.tradie.name == 'Rizza'
assert d.tradie.occupation == 'sparkie'
如果我没记错的话,Python 不支持尾调用优化。 所以在Python中使用深度递归函数时请注意。 对于小例子,应该没问题。
更新
另一个版本。 object_hook
具有嵌套的魔力。 我更喜欢这个版本,因为我可以直接将它们提供给jinja2
模板引擎。
import json
class _DotDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def dot(data=None):
if data is []:
return []
return json.loads(json.dumps(data), object_hook=_DotDict) if data else _DotDict()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.