我希望能够做到这样的事情:
from dotDict import dotdictify
life = {'bigBang':
{'stars':
{'planets': []}
}
}
dotdictify(life)
# This would be the regular way:
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}
# But how can we make this work?
life.bigBang.stars.planets.earth = {'singleCellLife': {}}
#Also creating new child objects if none exist, using the following syntax:
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles':{},'mammals':{}}
我的动机是改进代码的简洁性,如果可能的话,使用与Javascript类似的语法来访问JSON对象,以实现高效的跨平台开发。 (我也使用Py2JS和类似的。)
答案 0 :(得分:47)
这是创造这种体验的一种方式:
class DotDictify(dict):
MARKER = object()
def __init__(self, value=None):
if value is None:
pass
elif isinstance(value, dict):
for key in value:
self.__setitem__(key, value[key])
else:
raise TypeError('expected dict')
def __setitem__(self, key, value):
if isinstance(value, dict) and not isinstance(value, DotDictify):
value = DotDictify(value)
super(DotDictify, self).__setitem__(key, value)
def __getitem__(self, key):
found = self.get(key, DotDictify.MARKER)
if found is DotDictify.MARKER:
found = DotDictify()
super(DotDictify, self).__setitem__(key, found)
return found
__setattr__, __getattr__ = __setitem__, __getitem__
if __name__ == '__main__':
life = {'bigBang':
{'stars':
{'planets': {} # Value changed from []
}
}
}
life = DotDictify(life)
print(life.bigBang.stars.planets) # -> []
life.bigBang.stars.planets.earth = {'singleCellLife' : {}}
print(life.bigBang.stars.planets) # -> {'earth': {'singleCellLife': {}}}
答案 1 :(得分:2)
下面是嵌套属性字典的另一个实现(受到Curt Hagenlocher的回答的启发,被剥离到必要的):
class AttrDict(dict):
""" Nested Attribute Dictionary
A class to convert a nested Dictionary into an object with key-values
accessibly using attribute notation (AttrDict.attribute) in addition to
key notation (Dict["key"]). This class recursively sets Dicts to objects,
allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
"""
def __init__(self, mapping=None):
super(AttrDict, self).__init__()
if mapping is not None:
for key, value in mapping.items():
self.__setitem__(key, value)
def __setitem__(self, key, value):
if isinstance(value, dict):
value = AttrDict(value)
super(AttrDict, self).__setitem__(key, value)
self.__dict__[key] = value # for code completion in editors
def __getattr__(self, item):
try:
return self.__getitem__(item)
except KeyError:
raise AttributeError(item)
__setattr__ = __setitem__
这适用于Python 2和3:
life = AttrDict({'bigBang': {'stars': {'planets': {}}}})
life['bigBang']['stars']['planets'] = {'earth': {'singleCellLife': {}}}
life.bigBang.stars.planets.earth.multiCellLife = {'reptiles': {}, 'mammals': {}}
print(life.bigBang.stars.planets.earth)
# -> {'singleCellLife': {}, 'multiCellLife': {'mammals': {}, 'reptiles': {}}}
在Python3中需要将__getattr__
中的KeyError转换为AttributeError,以便在找不到该属性的情况下hasattr
也能正常工作:
hasattr(life, 'parallelUniverse')
# --> False
答案 2 :(得分:2)
有一个程序包可以完全满足您的需求,还可以做更多的事情,它称为Prodict。
from prodict import Prodict
life_dict = {'bigBang':
{'stars':
{'planets': []}
}
}
life = Prodict.from_dict(life_dict)
print(life.bigBang.stars.planets)
# prints []
# you can even add new properties dynamically
life.bigBang.galaxies = []
PS:我是《禁令》的作者。
答案 3 :(得分:0)
这是另一种解决方案:
from typing import Dict, Any
class PropertyTree: pass
def dict_to_prop_tree(yaml_config: Dict[str, Any]) -> PropertyTree:
tree = PropertyTree()
for key, value in yaml_config.items():
if type(value) == dict:
setattr(tree, key, dict_to_obj_tree(value))
elif type(value) == list:
setattr(tree, key, [dict_to_obj_tree(v) for v in value])
else:
setattr(tree, key, value)
return tree
然后在python控制台中:
d={'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5, 'f': {'g': 6}, 'h': {}, 'j': 7}}
tree=dict_to_prop_tree(d)
tree.a
tree.c.f.g
打印正确的值
答案 4 :(得分:0)
class AccessMode(dict):
def __init__(self, mapping = None):
super(AccessMode, self).__init__()
if mapping is not None:
if isinstance(mapping, dict):
for k, v in mapping.items():
if isinstance(v, dict):
v = AccessMode(v)
self.__setitem__(k, v)
else:
print "TypeError: Input must be a 'dict' type.\n"
def __setitem__(self, k, v):
super(AccessMode, self).__setitem__(k, v)
def __getitem__(self, k):
return super(AccessMode,self).__getitem__(k)
def __missing__(self, k):
tmp = AccessMode()
self[k] = tmp
return tmp
__setattr__, __getattr__ = __setitem__, __getitem__