如何通过点“。”访问Python词典成员?
例如,mydict['val']
我不想写,而是想写mydict.val
。
我也想以这种方式访问嵌套的字典。例如
mydict.mydict2.val
将指
mydict = { 'mydict2': { 'val': ... } }
{"my.key":"value"}
?还是当关键字是关键字时,例如“ from”?我已经考虑过几次了,这是更多的问题和疑难解答,而不是预期的好处。
如何通过点“。”访问Python词典成员?
例如,mydict['val']
我不想写,而是想写mydict.val
。
我也想以这种方式访问嵌套的字典。例如
mydict.mydict2.val
将指
mydict = { 'mydict2': { 'val': ... } }
{"my.key":"value"}
?还是当关键字是关键字时,例如“ from”?我已经考虑过几次了,这是更多的问题和疑难解答,而不是预期的好处。
Answers:
您可以使用我刚刚制作的此类进行操作。通过此类,您可以Map
像其他字典(包括json序列化)一样使用该对象,也可以使用点符号。希望对您有所帮助:
class Map(dict):
"""
Example:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
"""
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self[k] = v
if kwargs:
for k, v in kwargs.iteritems():
self[k] = v
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
用法示例:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']
.iteritems()
至.items()
AttributeError
如果属性不存在,它将不会提高。相反,它将返回None
。
self.update(*args,**kwargs)
。另外,您可以添加__missing__(self,key): value=self[key]= type(self)(); return value
。然后,您可以使用点表示法添加缺少的条目。如果您希望它是可选取的,则可以添加__getstate__
和__setstate__
hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding
__getattr__`
我一直将其保存在util文件中。您也可以在自己的类中将其用作混合。
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'
mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'
d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.bar
引发属性错误,但d.foo
工作正常。
python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
get
确实是一个坏主意,因为它会返回None
而不是对丢失的项目引发错误……
dotmap
通过安装pip
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
这意味着,如果您要访问的内容已经dict
存在,可以将其转换DotMap
为易于访问的内容:
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city
最后,它会自动创建新的子DotMap
实例,因此您可以执行以下操作:
m = DotMap()
m.people.steve.age = 31
全面披露:我是DotMap的创建者。我创建它是因为Bunch
缺少这些功能
DotMap
创建,这样可以节省时间,并在您具有多个层次结构时使代码更简洁dict
并将所有子dict
实例递归转换为DotMap
{"test.foo": "bar"}
可以通过mymap.test.foo
那访问。那太棒了。将平面地图转换为深层地图然后将DotMap应用于该地图需要一些回归,但这是值得的!
DotMap
自动完成功能最有效。我使用Sublime Text,它可以自动完成以前键入的关键字。
**kwargs
或的字典提取功能c = {**a, **b}
。实际上,它会悄悄地失败,提取时的行为就像一个空字典。
m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));
了测试,并且得到了预期的效果2 3
。如果您有证明有效的破损案例,dict()
但不适用于DotMap()
,请将代码提交至GitHub中的Issues标签。
Fabric具有非常好的,最小的实现。扩展它以允许嵌套访问,我们可以使用defaultdict
,结果看起来像这样:
from collections import defaultdict
class AttributeDict(defaultdict):
def __init__(self):
super(AttributeDict, self).__init__(AttributeDict)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
self[key] = value
如下使用它:
keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234
这就详细说明了库格尔的回答:“从命令和实现中得出__getattr__
和__setattr__
”。现在您知道了!
dotdict
它允许将现有的dict
递归对象:gist.github.com/miku/...
我尝试了这个:
class dotdict(dict):
def __getattr__(self, name):
return self[name]
你可以试试 __getattribute__
。
让每个字典都使用点分隔符就足够了,如果您想从多层字典中初始化它,也可以尝试实现__init__
。
def docdict(name):
前,然后`如果isinstance(姓名,字典):返回DotDict(名)的回报名`
class dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
别。属性访问和索引编制在Python中是分开的,您不希望它们执行相同的操作。namedtuple
如果您有一些应该具有可访问属性的东西,并使用[]
表示法从字典中获取一项,则创建一个类(可能是由制成的类)。
如果要腌制修改后的字典,则需要在上述答案中添加一些状态方法:
class DotDict(dict):
"""dot.notation access to dictionary attributes"""
def __getattr__(self, attr):
return self.get(attr)
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
def __getstate__(self):
return self
def __setstate__(self, state):
self.update(state)
self.__dict__ = self
__getattr__ = dict.get
以库格尔(Kugel)的答案为基础,并考虑迈克·格雷厄姆(Mike Graham)的警告语,如果我们做包装纸怎么办?
class DictWrap(object):
""" Wrap an existing dict, or create a new one, and access with either dot
notation or key lookup.
The attribute _data is reserved and stores the underlying dictionary.
When using the += operator with create=True, the empty nested dict is
replaced with the operand, effectively creating a default dictionary
of mixed types.
args:
d({}): Existing dict to wrap, an empty dict is created by default
create(True): Create an empty, nested dict instead of raising a KeyError
example:
>>>dw = DictWrap({'pp':3})
>>>dw.a.b += 2
>>>dw.a.b += 2
>>>dw.a['c'] += 'Hello'
>>>dw.a['c'] += ' World'
>>>dw.a.d
>>>print dw._data
{'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}
"""
def __init__(self, d=None, create=True):
if d is None:
d = {}
supr = super(DictWrap, self)
supr.__setattr__('_data', d)
supr.__setattr__('__create', create)
def __getattr__(self, name):
try:
value = self._data[name]
except KeyError:
if not super(DictWrap, self).__getattribute__('__create'):
raise
value = {}
self._data[name] = value
if hasattr(value, 'items'):
create = super(DictWrap, self).__getattribute__('__create')
return DictWrap(value, create)
return value
def __setattr__(self, name, value):
self._data[name] = value
def __getitem__(self, key):
try:
value = self._data[key]
except KeyError:
if not super(DictWrap, self).__getattribute__('__create'):
raise
value = {}
self._data[key] = value
if hasattr(value, 'items'):
create = super(DictWrap, self).__getattribute__('__create')
return DictWrap(value, create)
return value
def __setitem__(self, key, value):
self._data[key] = value
def __iadd__(self, other):
if self._data:
raise TypeError("A Nested dict will only be replaced if it's empty")
else:
return other
最近,我遇到了“ 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)更有效,如点阵图,当嵌套的字典较大时,它们会生成python递归错误。
链接到库和详细信息:https : //pypi.org/project/python-box/
语言本身不支持此功能,但有时这仍然是有用的要求。除了Bunch食谱之外,您还可以编写一些小方法,该方法可以使用点分字符串来访问字典:
def get_var(input_dict, accessor_string):
"""Gets data from a dictionary using a dotted accessor-string"""
current_data = input_dict
for chunk in accessor_string.split('.'):
current_data = current_data.get(chunk, {})
return current_data
这将支持以下内容:
>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
为了建立epool的答案,此版本允许您通过点运算符访问内部的所有dict:
foo = {
"bar" : {
"baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
}
}
例如,foo.bar.baz[1].baba
return "loo"
。
class Map(dict):
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
if isinstance(v, dict):
v = Map(v)
if isinstance(v, list):
self.__convert(v)
self[k] = v
if kwargs:
for k, v in kwargs.iteritems():
if isinstance(v, dict):
v = Map(v)
elif isinstance(v, list):
self.__convert(v)
self[k] = v
def __convert(self, v):
for elem in xrange(0, len(v)):
if isinstance(v[elem], dict):
v[elem] = Map(v[elem])
elif isinstance(v[elem], list):
self.__convert(v[elem])
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
iteritems()
同items()
和xrange()
同range()
def dict_to_object(dick):
# http://stackoverflow.com/a/1305663/968442
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
return Struct(**dick)
如果决定永久将其转换dict
为对象,则应该这样做。您可以在访问之前创建一个一次性对象。
d = dict_to_object(d)
我最终都尝试了AttrDict和 Bunch库,发现它们是我使用速度减慢的方法。经过一个朋友和我的研究,我们发现编写这些库的主要方法导致该库通过嵌套对象积极地递归并在整个字典对象中进行复制。考虑到这一点,我们进行了两个关键更改。1)我们使属性延迟加载; 2)我们创建轻量级代理对象的副本,而不是创建字典对象的副本。这是最终的实现。使用此代码的性能提升令人难以置信。使用AttrDict或Bunch时,仅这两个库分别消耗了我的请求时间的1/2和1/3(什么!?)。这段代码将时间减少到几乎没有(在0.5ms范围内)。当然,这取决于您的需求,但是如果您在代码中大量使用此功能,
class DictProxy(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return wrap(self.obj[key])
def __getattr__(self, key):
try:
return wrap(getattr(self.obj, key))
except AttributeError:
try:
return self[key]
except KeyError:
raise AttributeError(key)
# you probably also want to proxy important list properties along like
# items(), iteritems() and __len__
class ListProxy(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return wrap(self.obj[key])
# you probably also want to proxy important list properties along like
# __iter__ and __len__
def wrap(value):
if isinstance(value, dict):
return DictProxy(value)
if isinstance(value, (tuple, list)):
return ListProxy(value)
return value
通过https://stackoverflow.com/users/704327/michael-merickel查看此处的原始实现。
还要注意的另一件事是,此实现非常简单,并没有实现您可能需要的所有方法。您需要根据需要在DictProxy或ListProxy对象上编写这些内容。
我想将自己的解决方案付诸实践:
https://github.com/skorokithakis/jsane
它使您可以将JSON解析为可以访问的内容with.attribute.lookups.like.this.r()
,主要是因为在开始使用JSON 之前我还没有看到这个答案。
KeyError
其中之一就是引发错误,当访问一个不存在的键时,它所要做的就是返回None
类似于JS行为的返回值。我非常喜欢阅读和写作中的自动生存。您的图书馆最接近理想。
不是直接回答OP的问题,而是受到某些人的启发,也许对某些人有用。.我已经使用内部__dict__
方法创建了基于对象的解决方案(绝不优化代码)
payload = {
"name": "John",
"location": {
"lat": 53.12312312,
"long": 43.21345112
},
"numbers": [
{
"role": "home",
"number": "070-12345678"
},
{
"role": "office",
"number": "070-12345679"
}
]
}
class Map(object):
"""
Dot style access to object members, access raw values
with an underscore e.g.
class Foo(Map):
def foo(self):
return self.get('foo') + 'bar'
obj = Foo(**{'foo': 'foo'})
obj.foo => 'foobar'
obj._foo => 'foo'
"""
def __init__(self, *args, **kwargs):
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self.__dict__[k] = v
self.__dict__['_' + k] = v
if kwargs:
for k, v in kwargs.iteritems():
self.__dict__[k] = v
self.__dict__['_' + k] = v
def __getattribute__(self, attr):
if hasattr(self, 'get_' + attr):
return object.__getattribute__(self, 'get_' + attr)()
else:
return object.__getattribute__(self, attr)
def get(self, key):
try:
return self.__dict__.get('get_' + key)()
except (AttributeError, TypeError):
return self.__dict__.get(key)
def __repr__(self):
return u"<{name} object>".format(
name=self.__class__.__name__
)
class Number(Map):
def get_role(self):
return self.get('role')
def get_number(self):
return self.get('number')
class Location(Map):
def get_latitude(self):
return self.get('lat') + 1
def get_longitude(self):
return self.get('long') + 1
class Item(Map):
def get_name(self):
return self.get('name') + " Doe"
def get_location(self):
return Location(**self.get('location'))
def get_numbers(self):
return [Number(**n) for n in self.get('numbers')]
# Tests
obj = Item({'foo': 'bar'}, **payload)
assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112
for n in obj.numbers:
assert type(n) == Number
if n.role == 'home':
assert n.number == "070-12345678"
if n.role == 'office':
assert n.number == "070-12345679"
此解决方案是对epool提供的解决方案的改进,以解决OP以一致方式访问嵌套dict的要求。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
。
这也适用于嵌套字典,并确保稍后附加的字典具有相同的行为:
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的答案非常简洁,但是不能被腌制或(深度)复制,并且返回None
因缺少键。下面的代码解决了这个问题。
编辑:我没有看到上面的答案完全相同的点(已投票)。我将答案留在这里以供参考。
class dotdict(dict):
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
一种精致的解决方案
class DotDict(dict):
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def __getattr__(self, key):
def typer(candidate):
if isinstance(candidate, dict):
return DotDict(candidate)
if isinstance(candidate, str): # iterable but no need to iter
return candidate
try: # other iterable are processed as list
return [typer(item) for item in candidate]
except TypeError:
return candidate
return candidate
return typer(dict.get(self, key))
d[a][b][c]
替换为d[a, b, c]
。