[英]How to convert a nested python dictionary into a simple namespace?
Let's say I have a nested dictionary with depth N. How can I convert each inner nested 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"))
Are there better alternatives to make the keys of the dictionary accessible per dot notation (eg example_input.key0a) if the example_input dict is given - without having external dependencies?如果给出了 example_input 字典,是否有更好的替代方法可以使字典的键可以按点表示法访问(例如 example_input.key0a) - 没有外部依赖项?
Just answering your second (last) question - it is popular topic, there exist many different projects (non-standard) that make your dictionary into dot notation object.只是回答你的第二个(最后一个)问题 - 这是一个热门话题,存在许多不同的项目(非标准),可以让你的字典变成点符号 object。
For example this one - attrdict .例如这个 one- attrdict 。 Install it through
pip install attrdict
.通过
pip install attrdict
安装它。
Example of usage:使用示例:
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
If you wonder how module like attrdict is implemented, then I wrote a very simple implementation of similar functionality (of course real attrdict
should be more rich):如果你想知道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: Output:
namespace(key0a='test',
key0b=namespace(key1a=namespace(key2a='end', key2b='test'), key1b='test'),
something='else')
2022 answer: now there is a tiny, relatively fast library I have published, called dotwiz
, which alternatively can be used to provide easy dot access for a python dict
object. 2022 回答:现在我发布了一个很小、相对较快的库,称为
dotwiz
,它也可用于为 python dict
object 提供简单的点访问。
It should, coincidentally, be a little faster than the other options -- I've added a quick and dirty benchmark code I put together using the timeit
module below, timing against both a attrdict
and SimpleNamespace
approach -- the latter of which actually performs pretty solid in times.巧合的是,它应该比其他选项快一点——我添加了一个快速而肮脏的基准代码,我使用下面的
timeit
模块放在一起,针对attrdict
和SimpleNamespace
方法计时——后者实际上执行有时相当稳固。
Note that I had to modify the
parse
function slightly, so that it handles nesteddict
s within alist
object, for example.请注意,例如,我必须稍微修改
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'
Here are the results, on my M1 Mac Pro laptop:以下是我的 M1 Mac Pro 笔记本电脑上的结果:
attrdict: 0.69
dotwiz: 1.3
SimpleNamespace: 1.38
-- Get
attrdict: 6.06
dotwiz: 0.06
SimpleNamespace: 0.06
The dotwiz library can be installed with pip
: dotwiz库可以与
pip
一起安装:
$ pip install dotwiz
Based on mujjija's solution this is what I came up with.基于 mujjija 的解决方案,这就是我想出的。 Full code below
完整代码如下
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'
If I'm not mistaken, Python doesn't support Tail Call Optimization.如果我没记错的话,Python 不支持尾调用优化。 So please be careful when using deep recursive functions in Python.
所以在Python中使用深度递归函数时请注意。 For small examples, it should be fine.
对于小例子,应该没问题。
Update更新
Another version.另一个版本。
object_hook
does the magic of nesting. object_hook
具有嵌套的魔力。 I prefer this version because I can directly feed them to the jinja2
template engine.我更喜欢这个版本,因为我可以直接将它们提供给
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.