[英]How to use a dot "." to access members of dictionary?
How do I make Python dictionary members accessible via a dot "."?如何使 Python 字典成员可以通过点“.”访问?
For example, instead of writing mydict['val']
, I'd like to write mydict.val
.例如,我不想写mydict['val']
,而是写mydict.val
。
Also I'd like to access nested dicts this way.我也想以这种方式访问嵌套的字典。 For example例如
mydict.mydict2.val
would refer to会指
mydict = { 'mydict2': { 'val': ... } }
I've always kept this around in a util file.我一直把它保存在一个 util 文件中。 You can use it as a mixin on your own classes too.你也可以在你自己的类中使用它作为一个 mixin。
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'
You can do it using this class I just made.你可以使用我刚刚制作的这个类来做到这一点。 With this class you can use the Map
object like another dictionary(including json serialization) or with the dot notation.使用此类,您可以像使用另一个字典(包括 json 序列化)或使用点符号一样使用Map
对象。 I hope to help you:我希望能帮助你:
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]
Usage examples:使用示例:
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']
Install dotmap
via pip
通过dotmap
安装pip
图
pip install dotmap
It does everything you want it to do and subclasses dict
, so it operates like a normal dictionary:它做你想做的所有事情,并继承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'
On top of that, you can convert it to and from dict
objects:最重要的是,您可以将其与dict
对象相互转换:
d = m.toDict()
m = DotMap(d) # automatic conversion in constructor
This means that if something you want to access is already in dict
form, you can turn it into a DotMap
for easy access:这意味着如果您要访问的内容已经是dict
形式,您可以将其转换为DotMap
以便于访问:
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city
Finally, it automatically creates new child DotMap
instances so you can do things like this:最后,它会自动创建新的子DotMap
实例,因此您可以执行以下操作:
m = DotMap()
m.people.steve.age = 31
Full disclosure: I am the creator of the DotMap .全面披露:我是DotMap的创建者。 I created it because Bunch
was missing these features我创建它是因为Bunch
缺少这些功能
DotMap
creation, which saves time and makes for cleaner code when you have a lot of hierarchy自动创建子DotMap
,当您有很多层次结构时,这可以节省时间并让代码更简洁dict
and recursively converting all child dict
instances to DotMap
从dict
构造并递归地将所有子dict
实例转换为DotMap
Use SimpleNamespace
:使用SimpleNamespace
:
>>> from types import SimpleNamespace
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])
Fabric has a really nice, minimal implementation . Fabric有一个非常好的、最小的实现。 Extending that to allow for nested access, we can use a defaultdict
, and the result looks something like this:扩展它以允许嵌套访问,我们可以使用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
Make use of it as follows:如下使用它:
keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234
That elaborates a bit on Kugel's answer of "Derive from dict and and implement __getattr__
and __setattr__
".这详细说明了 Kugel 对“从 dict 派生并实现__getattr__
和__setattr__
”的回答。 Now you know how!现在你知道怎么做了!
I tried this:我试过这个:
class dotdict(dict):
def __getattr__(self, name):
return self[name]
you can try __getattribute__
too.你也可以试试__getattribute__
。
make every dict a type of dotdict would be good enough, if you want to init this from a multi-layer dict, try implement __init__
too.使每个 dict 成为一种 dotdict 就足够了,如果您想从多层 dict 初始化它,请尝试实现__init__
。
I recently came across the ' Box ' library which does the same thing.我最近遇到了做同样事情的' Box '库。
Installation command : pip install python-box
安装命令: pip install python-box
Example:例子:
from box import Box
mydict = {"key1":{"v1":0.375,
"v2":0.625},
"key2":0.125,
}
mydict = Box(mydict)
print(mydict.key1.v1)
I found it to be more effective than other existing libraries like dotmap, which generate python recursion error when you have large nested dicts.我发现它比其他现有库(如 dotmap)更有效,当你有大型嵌套字典时,它会生成 python 递归错误。
link to library and details: https://pypi.org/project/python-box/链接到库和详细信息: https ://pypi.org/project/python-box/
If you want to pickle your modified dictionary, you need to add few state methods to above answers:如果要腌制修改后的字典,则需要在上述答案中添加一些状态方法:
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
Don't.不。 Attribute access and indexing are separate things in Python, and you shouldn't want them to perform the same.属性访问和索引在 Python 中是不同的事情,您不应该希望它们执行相同的操作。 Make a class (possibly one made by namedtuple
) if you have something that should have accessible attributes and use []
notation to get an item from a dict.如果您有一些应该具有可访问属性并使用[]
表示法从字典中获取项目的东西,请创建一个类(可能由namedtuple
创建一个)。
Building on Kugel's answer and taking Mike Graham's words of caution into consideration, what if we make a wrapper?基于 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
To build upon epool's answer, this version allows you to access any dict inside via the dot operator:为了建立 epool 的答案,此版本允许您通过点运算符访问内部的任何 dict:
foo = {
"bar" : {
"baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
}
}
For instance, foo.bar.baz[1].baba
returns "loo"
.例如, foo.bar.baz[1].baba
返回"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.items():
if isinstance(v, dict):
v = Map(v)
if isinstance(v, list):
self.__convert(v)
self[k] = v
if kwargs:
for k, v in kwargs.items():
if isinstance(v, dict):
v = Map(v)
elif isinstance(v, list):
self.__convert(v)
self[k] = v
def __convert(self, v):
for elem in range(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]
You can achieve this using SimpleNamespace您可以使用 SimpleNamespace 实现此目的
from types import SimpleNamespace
# Assign values
args = SimpleNamespace()
args.username = 'admin'
# Retrive values
print(args.username) # output: admin
Use __getattr__
, very simple, works in Python 3.4.3使用__getattr__
,非常简单,适用于 Python 3.4.3
class myDict(dict):
def __getattr__(self,val):
return self[val]
blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)
Output:输出:
10000
StackOverflow
I like the Munch and it gives lot of handy options on top of dot access.我喜欢Munch ,它在点访问之上提供了许多方便的选项。
import munch进口蒙克
temp_1 = {'person': { 'fname': 'senthil', 'lname': 'ramalingam'}} temp_1 = {'person':{'fname':'senthil','lname':'ramalingam'}}
dict_munch = munch.munchify(temp_1) dict_munch = munch.munchify(temp_1)
dict_munch.person.fname dict_munch.person.fname
The language itself doesn't support this, but sometimes this is still a useful requirement.语言本身不支持这一点,但有时这仍然是一个有用的要求。 Besides the Bunch recipe, you can also write a little method which can access a dictionary using a dotted string:除了 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
which would support something like this:这将支持这样的事情:
>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
I ended up trying BOTH the AttrDict and the Bunch libraries and found them to be way to slow for my uses.我最终尝试了AttrDict和Bunch库,发现它们会降低我的使用速度。 After a friend and I looked into it, we found that the main method for writing these libraries results in the library aggressively recursing through a nested object and making copies of the dictionary object throughout.在我和一个朋友调查之后,我们发现编写这些库的主要方法导致库通过嵌套对象积极递归并在整个过程中复制字典对象。 With this in mind, we made two key changes.考虑到这一点,我们进行了两项关键更改。 1) We made attributes lazy-loaded 2) instead of creating copies of a dictionary object, we create copies of a light-weight proxy object. 1)我们使属性延迟加载 2)我们不是创建字典对象的副本,而是创建轻量级代理对象的副本。 This is the final implementation.这是最终的实现。 The performance increase of using this code is incredible.使用此代码的性能提升令人难以置信。 When using AttrDict or Bunch, these two libraries alone consumed 1/2 and 1/3 respectively of my request time(what!?).当使用 AttrDict 或 Bunch 时,这两个库分别消耗了我请求时间的 1/2 和 1/3(什么!?)。 This code reduced that time to almost nothing(somewhere in the range of 0.5ms).这段代码将该时间减少到几乎没有(在 0.5 毫秒的范围内)。 This of course depends on your needs, but if you are using this functionality quite a bit in your code, definitely go with something simple like this.这当然取决于您的需求,但如果您在代码中大量使用此功能,那么一定要使用像这样简单的东西。
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
See the original implementation here by https://stackoverflow.com/users/704327/michael-merickel .请参阅https://stackoverflow.com/users/704327/michael-merickel此处的原始实现。
The other thing to note, is that this implementation is pretty simple and doesn't implement all of the methods you might need.需要注意的另一件事是,此实现非常简单,并没有实现您可能需要的所有方法。 You'll need to write those as required on the DictProxy or ListProxy objects.您需要根据需要在 DictProxy 或 ListProxy 对象上编写这些内容。
This solution is a refinement upon the one offered by epool to address the requirement of the OP to access nested dicts in a consistent manner.该解决方案是对epool提供的解决方案的改进,以解决 OP 以一致方式访问嵌套字典的要求。 The solution by epool did not allow for accessing nested dicts. 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]
With this class, one can now do something like: ABCD
.有了这个类,现在可以做类似的事情: ABCD
。
def dict_to_object(dick):
# http://stackoverflow.com/a/1305663/968442
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
return Struct(**dick)
If one decides to permanently convert that dict
to object this should do.如果一个人决定将该dict
永久转换为对象,则应该这样做。 You can create a throwaway object just before accessing.您可以在访问之前创建一次性对象。
d = dict_to_object(d)
For infinite levels of nesting of dicts, lists, lists of dicts, and dicts of lists.对于字典、列表、字典列表和列表字典的无限嵌套级别。
It also supports pickling它还支持酸洗
This is an extension of this answer .这是这个答案的延伸。
class DotDict(dict):
# https://stackoverflow.com/a/70665030/913098
"""
Example:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
Iterable are assumed to have a constructor taking list as input.
"""
def __init__(self, *args, **kwargs):
super(DotDict, self).__init__(*args, **kwargs)
args_with_kwargs = []
for arg in args:
args_with_kwargs.append(arg)
args_with_kwargs.append(kwargs)
args = args_with_kwargs
for arg in args:
if isinstance(arg, dict):
for k, v in arg.items():
self[k] = v
if isinstance(v, dict):
self[k] = DotDict(v)
elif isinstance(v, str) or isinstance(v, bytes):
self[k] = v
elif isinstance(v, Iterable):
klass = type(v)
map_value: List[Any] = []
for e in v:
map_e = DotDict(e) if isinstance(e, dict) else e
map_value.append(map_e)
self[k] = klass(map_value)
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(DotDict, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(DotDict, self).__delitem__(key)
del self.__dict__[key]
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
self.__dict__.update(d)
if __name__ == "__main__":
import pickle
def test_map():
d = {
"a": 1,
"b": {
"c": "d",
"e": 2,
"f": None
},
"g": [],
"h": [1, "i"],
"j": [1, "k", {}],
"l":
[
1,
"m",
{
"n": [3],
"o": "p",
"q": {
"r": "s",
"t": ["u", 5, {"v": "w"}, ],
"x": ("z", 1)
}
}
],
}
map_d = DotDict(d)
w = map_d.l[2].q.t[2].v
assert w == "w"
pickled = pickle.dumps(map_d)
unpickled = pickle.loads(pickled)
assert unpickled == map_d
kwargs_check = DotDict(a=1, b=[dict(c=2, d="3"), 5])
assert kwargs_check.b[0].d == "3"
kwargs_and_args_check = DotDict(d, a=1, b=[dict(c=2, d="3"), 5])
assert kwargs_and_args_check.l[2].q.t[2].v == "w"
assert kwargs_and_args_check.b[0].d == "3"
test_map()
This also works with nested dicts and makes sure that dicts which are appended later behave the same:这也适用于嵌套的字典,并确保稍后附加的字典表现相同:
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__
Using namedtuple
allows dot access.使用namedtuple
允许点访问。
It is like a lightweight object which also has the properties of a tuple.它就像一个轻量级对象,也具有元组的属性。
It allows to define properties and access them using the dot operator .它允许定义属性并使用点运算符访问它们。
from collections import namedtuple
Data = namedtuple('Data', ['key1', 'key2'])
dataObj = Data(val1, key2=val2) # can instantiate using keyword arguments and positional arguments
Access using dot operator使用点运算符访问
dataObj.key1 # Gives val1
datObj.key2 # Gives val2
Access using tuple indices使用元组索引访问
dataObj[0] # Gives val1
dataObj[1] # Gives val2
But remember this is a tuple;但请记住这是一个元组; not a dict .不是 dict 。 So the below code will give error所以下面的代码会报错
dataObj['key1'] # Gives TypeError: tuple indices must be integers or slices, not str
Refer: namedtuple参考: namedtuple
这是一个老问题,但我最近发现sklearn
有一个可通过密钥访问的实现版本dict
,即Bunch
https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html#sklearn.utils。束
Simplest solution.最简单的解决方案。
Define a class with only pass statement in it.定义一个只有 pass 语句的类。 Create object for this class and use dot notation.为此类创建对象并使用点表示法。
class my_dict:
pass
person = my_dict()
person.id = 1 # create using dot notation
person.phone = 9999
del person.phone # Remove a property using dot notation
name_data = my_dict()
name_data.first_name = 'Arnold'
name_data.last_name = 'Schwarzenegger'
person.name = name_data
person.name.first_name # dot notation access for nested properties - gives Arnold
One simple way to get dot access (but not array access), is to use a plain object in Python.获得点访问(但不是数组访问)的一种简单方法是在 Python 中使用普通对象。 Like this:像这样:
class YourObject:
def __init__(self, *args, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
...and use it like this: ...并像这样使用它:
>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"
... to convert it to a dict: ...将其转换为字典:
>>> print(obj.__dict__)
{"key": "value"}
The answer of @derek73 is very neat, but it cannot be pickled nor (deep)copied, and it returns None
for missing keys. @derek73 的答案非常简洁,但它不能被腌制或(深度)复制,并且它返回None
丢失键。 The code below fixes this.下面的代码解决了这个问题。
Edit: I did not see the answer above that addresses the exact same point (upvoted).编辑:我没有看到上面的答案解决了完全相同的问题(赞成)。 I'm leaving the answer here for reference.我将答案留在这里以供参考。
class dotdict(dict):
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
I just needed to access a dictionary using a dotted path string, so I came up with:我只需要使用点路径字符串访问字典,所以我想出了:
def get_value_from_path(dictionary, parts):
""" extracts a value from a dictionary using a dotted path string """
if type(parts) is str:
parts = parts.split('.')
if len(parts) > 1:
return get_value_from_path(dictionary[parts[0]], parts[1:])
return dictionary[parts[0]]
a = {'a':{'b':'c'}}
print(get_value_from_path(a, 'a.b')) # c
I'd like to throw my own solution into the ring:我想把我自己的解决方案扔进戒指:
https://github.com/skorokithakis/jsane https://github.com/skorokithakis/jsane
It allows you to parse JSON into something you can access with.attribute.lookups.like.this.r()
, mostly because I hadn't seen this answer before starting to work on it.它允许您将 JSON 解析为您可以使用with.attribute.lookups.like.this.r()
访问的with.attribute.lookups.like.this.r()
,主要是因为我在开始研究之前没有看到这个答案。
Not a direct answer to the OP's question, but inspired by and perhaps useful for some.. I've created an object-based solution using the internal __dict__
(In no way optimized code)不是对 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"
Here's my version of @derek73 answer .这是我的@derek73 answer版本。 I use dict.__getitem__
as __getattr__
so it still throws KeyError
, and im renaming dict public methods with " " prefix (surrounded with " " leads to special methods name conflict, like __get__
which would be treated as a descriptor method).我使用dict.__getitem__
作为__getattr__
所以它仍然抛出KeyError
,并且我用 " " 前缀重命名 dict 公共方法(用 " " 包围会导致特殊方法名称冲突,如__get__
将被视为描述符方法)。 Anyway you can't get completely clear namespace for keys as attributes due to crucial dict
base methods, so the solution isn't perfect but you can have keys - attributes like get
, pop
, items
etc.无论如何,由于关键的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
You can remove dict methods from DotDict namespace and keep using dict class methods, its useful also when you want to operate on other dict instances and want to use the same methods without extra check whether its DotDict or not, eg.您可以从 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
I just dug this up from a project I was working on a long time ago.我只是从很久以前从事的一个项目中挖掘出来的。 It could probably be optimized a bit, but here it goes.它可能会被优化一点,但它就在这里。
class DotNotation(dict):
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
def __init__(self, data):
if isinstance(data, str):
data = json.loads(data)
for name, value in data.items():
setattr(self, name, self._wrap(value))
def __getattr__(self, attr):
def _traverse(obj, attr):
if self._is_indexable(obj):
try:
return obj[int(attr)]
except:
return None
elif isinstance(obj, dict):
return obj.get(attr, None)
else:
return attr
if '.' in attr:
return reduce(_traverse, attr.split('.'), self)
return self.get(attr, None)
def _wrap(self, value):
if self._is_indexable(value):
# (!) recursive (!)
return type(value)([self._wrap(v) for v in value])
elif isinstance(value, dict):
return DotNotation(value)
else:
return value
@staticmethod
def _is_indexable(obj):
return isinstance(obj, (tuple, list, set, frozenset))
if __name__ == "__main__":
test_dict = {
"dimensions": {
"length": "112",
"width": "103",
"height": "42"
},
"meta_data": [
{
"id": 11089769,
"key": "imported_gallery_files",
"value": [
"https://example.com/wp-content/uploads/2019/09/unnamed-3.jpg",
"https://example.com/wp-content/uploads/2019/09/unnamed-2.jpg",
"https://example.com/wp-content/uploads/2019/09/unnamed-4.jpg"
]
}
]
}
dotted_dict = DotNotation(test_dict)
print(dotted_dict.dimensions.length) # => '112'
print(getattr(dotted_dict, 'dimensions.length')) # => '112'
print(dotted_dict.meta_data[0].key) # => 'imported_gallery_files'
print(getattr(dotted_dict, 'meta_data.0.key')) # => 'imported_gallery_files'
print(dotted_dict.meta_data[0].value) # => ['link1','link2','link2']
print(getattr(dotted_dict, 'meta_data.0.value')) # => ['link1','link2','link3']
print(dotted_dict.meta_data[0].value[2]) # => 'link3'
print(getattr(dotted_dict, 'meta_data.0.value.2')) # => 'link3'
My 2 cents: for my own purposes I developed minydra
, a simple command-line parser which includes a custom class MinyDict
(inspired by addict
):我的 2 美分:出于我自己的目的,我开发了minydra
,一个简单的命令行解析器,其中包括一个自定义类MinyDict
(受addict
启发):
In [1]: from minydra import MinyDict
In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).pretty_print(); args
╭──────────────────────────────╮
│ foo : bar │
│ yes.no.maybe : idontknow │
╰──────────────────────────────╯
Out[2]: {'foo': 'bar', 'yes.no.maybe': 'idontknow'}
In [3]: args.resolve().pretty_print(); args
╭──────────────────────────╮
│ foo : bar │
│ yes │
│ │no │
│ │ │maybe : idontknow │
╰──────────────────────────╯
Out[3]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}
In [4]: args.yes.no.maybe
Out[4]: "idontknow"
In [5]: "foo" in args
Out[5]: True
In [6]: "rick" in args
Out[6]: False
In [7]: args.morty is None
Out[7]: True
In [8]: args.items()
Out[8]: dict_items([('foo', 'bar'), ('yes', {'no': {'maybe': 'idontknow'}})])
It goes further than addict
by adding dumping/loading methods to/from json
yaml
and pickle
and also has a strict
mode in MinyDict.update()
to prevent the creation of new keys (this is useful to prevent typos in the command-line)它通过向/从json
yaml
和pickle
添加转储/加载方法比addict
更进一步,并且在MinyDict.update()
中还有一个strict
模式以防止创建新密钥(这对于防止命令行中的拼写错误很有用)
I dislike adding another log to a (more than) 10-year old fire, but I'd also check out the dotwiz
library, which I've recently released - just this year actually.我不喜欢将另一个日志添加到(超过)10 年的火灾中,但我也会查看我最近发布的dotwiz
库 - 实际上就在今年。
It's a relatively tiny library, which also performs really well for get (access) and set (create) times in benchmarks , at least as compared to other alternatives.这是一个相对较小的库,至少与其他替代方案相比,它在基准测试中的获取(访问)和设置(创建)时间方面也表现得非常好。
Install dotwiz
via pip
通过pip
安装dotwiz
pip install dotwiz
It does everything you want it to do and subclasses dict
, so it operates like a normal dictionary:它做你想做的所有事情,并继承dict
,所以它像普通字典一样运行:
from dotwiz import DotWiz
dw = DotWiz()
dw.hello = 'world'
dw.hello
dw.hello += '!'
# dw.hello and dw['hello'] now both return 'world!'
dw.val = 5
dw.val2 = 'Sam'
On top of that, you can convert it to and from dict
objects:最重要的是,您可以将其与dict
对象相互转换:
d = dw.to_dict()
dw = DotWiz(d) # automatic conversion in constructor
This means that if something you want to access is already in dict
form, you can turn it into a DotWiz
for easy access:这意味着如果您要访问的内容已经是dict
形式,您可以将其转换为DotWiz
以便于访问:
import json
json_dict = json.loads(text)
data = DotWiz(json_dict)
print data.location.city
Finally, something exciting I am working on is an existing feature request so that it automatically creates new child DotWiz
instances so you can do things like this:最后,我正在处理的令人兴奋的事情是现有功能请求,以便它自动创建新的子DotWiz
实例,以便您可以执行以下操作:
dw = DotWiz()
dw['people.steve.age'] = 31
dw
# ✫(people=✫(steve=✫(age=31)))
dotmap
与dotmap
I've added a quick and dirty performance comparison with dotmap
below.我在下面添加了与dotmap
的快速而肮脏的性能比较。
First, install both libraries with pip
:首先,使用pip
安装两个库:
pip install dotwiz dotmap
I came up with the following code for benchmark purposes:我想出了以下代码用于基准测试:
from timeit import timeit
from dotwiz import DotWiz
from dotmap import DotMap
d = {'hey': {'so': [{'this': {'is': {'pretty': {'cool': True}}}}]}}
dw = DotWiz(d)
# ✫(hey=✫(so=[✫(this=✫(is=✫(pretty={'cool'})))]))
dm = DotMap(d)
# DotMap(hey=DotMap(so=[DotMap(this=DotMap(is=DotMap(pretty={'cool'})))]))
assert dw.hey.so[0].this['is'].pretty.cool == dm.hey.so[0].this['is'].pretty.cool
n = 100_000
print('dotwiz (create): ', round(timeit('DotWiz(d)', number=n, globals=globals()), 3))
print('dotmap (create): ', round(timeit('DotMap(d)', number=n, globals=globals()), 3))
print('dotwiz (get): ', round(timeit("dw.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))
print('dotmap (get): ', round(timeit("dm.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))
Results, on my M1 Mac, running Python 3.10:结果,在我的 M1 Mac 上,运行 Python 3.10:
dotwiz (create): 0.189
dotmap (create): 1.085
dotwiz (get): 0.014
dotmap (get): 0.335
The implemention used by kaggle_environments is a function called structify
. kaggle_environments使用的实现是一个名为structify
的函数。
class Struct(dict):
def __init__(self, **entries):
entries = {k: v for k, v in entries.items() if k != "items"}
dict.__init__(self, entries)
self.__dict__.update(entries)
def __setattr__(self, attr, value):
self.__dict__[attr] = value
self[attr] = value
# Added benefit of cloning lists and dicts.
def structify(o):
if isinstance(o, list):
return [structify(o[i]) for i in range(len(o))]
elif isinstance(o, dict):
return Struct(**{k: structify(v) for k, v in o.items()})
return o
This may be useful for testing AI simulation agents in games like ConnectX这对于在ConnectX等游戏中测试 AI 模拟代理可能很有用
from kaggle_environments import structify
obs = structify({ 'remainingOverageTime': 60, 'step': 0, 'mark': 1, 'board': [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})
conf = structify({ 'timeout': 2, 'actTimeout': 2, 'agentTimeout': 60, 'episodeSteps': 1000, 'runTimeout': 1200, 'columns': 7, 'rows': 6, 'inarow': 4, '__raw_path__': '/kaggle_simulations/agent/main.py' })
def agent(obs, conf):
action = obs.step % conf.columns
return action
If you're already using pandas, you can construct a pandas Series or DataFrame from which you would be able to access items via the dot syntax:如果您已经在使用 pandas,您可以构建一个 pandas 系列或 DataFrame 可以通过点语法访问项目:
import pandas as pd
my_dictionary = pd.Series({
'key1': 'value1',
'key2': 'value2'
})
print(my_dictionary.key1)
# Output: value1
import pandas as pd
my_dictionary = pd.DataFrame({
'key1': {
'inner_key1': 'value1'
},
'key2': {
'inner_key2': 'value2'
}
})
print(my_dictionary.key1.inner_key1)
# Output: value1
Be aware that this probably works better with a normalised data structure (where each dictionary entry has the same structure).请注意,这可能更适用于规范化的数据结构(其中每个字典条目具有相同的结构)。 In the second example above, the resulting DataFrame is:在上面的第二个示例中,生成的 DataFrame 是:
key1 key2
inner_key1 value1 NaN
inner_key2 NaN value2
One could use dotsi , for full list, dict and recursive support, with some extension methods可以使用dotsi来获得完整列表、字典和递归支持,以及一些扩展方法
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'>
>>>
A solution kind of delicate一种微妙的解决方案
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))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.