How to use a dot “.” to access members of dictionary?
如何让Python字典的成员accessible点离开。""什么?P></
我也喜欢这套式词典接入方式。for exampleP></
1 | mydict.mydict2.val |
我推荐的P></
1 | mydict = { 'mydict2': { 'val': ... } } |
我一直把这个放在一个文件里。你也可以把它作为你自己的课程的混合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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' |
你可以用我刚上的这门课来做。使用这个类,您可以像使用另一个字典(包括JSON序列化)一样使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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] |
使用实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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'] |
通过
1 | pip install dotmap |
它做你想做的所有事情,并将
1 2 3 4 5 6 7 8 9 | 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' |
除此之外,您还可以将它与
1 2 | d = m.toDict() m = DotMap(d) # automatic conversion in constructor |
这意味着,如果您想要访问的内容已经以
1 2 3 4 | import json jsonDict = json.loads(text) data = DotMap(jsonDict) print data.location.city |
最后,它会自动创建新的子
1 2 | m = DotMap() m.people.steve.age = 31 |
与束比较
完全公开:我是dotmap的创建者。我创建它是因为
- 记住添加的顺序项并按该顺序迭代
- 自动创建子级
dotmap ,这节省了时间,并且在您有很多层次结构的情况下使代码更清晰。 - 从
dict 构造并递归地将所有子dict 实例转换为dotmap 。
从dict派生并实现
或者你可以用一束非常相似的。
我认为不可能用monkeypatch内置的dict类。
我试过这个:
1 2 3 | class dotdict(dict): def __getattr__(self, name): return self[name] |
你也可以试试
使每个dict都成为dotdict类型就足够了,如果您想从多层dict初始化它,也可以尝试实现
Fabric有一个非常好的、最小的实现。扩展到允许嵌套访问,我们可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
使用方法如下:
1 2 3 | keys = AttributeDict() keys.abc.xyz.x = 123 keys.abc.xyz.a.b.c = 234 |
这就阐述了库格尔的回答"从口述中派生并实现
如果您想对修改过的字典进行pickle处理,则需要在上述答案中添加一些状态方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
不要。属性访问和索引在Python中是分开的,您不应该希望它们执行相同的操作。如果你有一些应该具有可访问属性的东西,那么就创建一个类(可能是由
以库格尔的回答为基础,考虑到麦克格雷厄姆的谨慎言辞,如果我们做一个包装呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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 |
我喜欢Munch,它在点访问上提供了很多方便的选项。
import munch
temp_1 = {'person': { 'fname': 'senthil', 'lname': 'ramalingam'}}
dict_munch = munch.munchify(temp_1)
dict_munch.person.fname
语言本身不支持这一点,但有时这仍然是一个有用的需求。除了串配方,您还可以编写一个小方法,使用点线访问字典:
1 2 3 4 5 6 | 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 |
它将支持如下内容:
1 2 3 4 5 | >> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}} >> output = get_var(test_dict, 'thing.spam.foo.cheeze') >> print output 'bar' >> |
1 2 3 4 5 6 7 8 | def dict_to_object(dick): # http://stackoverflow.com/a/1305663/968442 class Struct: def __init__(self, **entries): self.__dict__.update(entries) return Struct(**dick) |
如果有人决定永久性地将
1 | d = dict_to_object(d) |
为了建立在epool答案的基础上,此版本允许您通过点运算符访问内部的任何dict:
1 2 3 4 5 | foo = { "bar" : { "baz" : [ {"boo" :"hoo"} , {"baba" :"loo"} ] } } |
例如,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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] |
使用
1 2 3 4 5 6 7 8 9 10 | class myDict(dict): def __getattr__(self,val): return self[val] blockBody=myDict() blockBody['item1']=10000 blockBody['item2']="StackOverflow" print(blockBody.item1) print(blockBody.item2) |
输出:
1 2 | 10000 StackOverflow |
我最终尝试了attrdict和bundle库,发现它们对于我的使用来说是一种放慢速度的方法。在我和一个朋友研究了它之后,我们发现编写这些库的主要方法导致库在一个嵌套的对象中急剧递归,并在整个过程中复制字典对象。考虑到这一点,我们做了两个关键的更改。1)我们将属性设置为lazy-loaded 2)而不是创建dictionary对象的副本,而是创建一个轻量级代理对象的副本。这是最终的实现。使用此代码的性能提高是令人难以置信的。当使用attrdict或bunch时,这两个库单独占用了我请求时间的1/2和1/3(什么!?)这段代码将时间缩短到几乎为零(在0.5毫秒的范围内)。当然,这取决于您的需求,但是如果您在代码中使用了这个功能,那么一定要使用类似这样的简单功能。
10请参阅https://stackoverflow.com/users/704327/michael-mericakel的原始实现。
另一个需要注意的是,这个实现非常简单,并且没有实现您可能需要的所有方法。您需要根据需要在dictproxy或listproxy对象上编写这些内容。
一种微妙的解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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)) |
我想把我自己的解决方案扔进戒指里:
https://github.com/skorokithakis/jsane
它允许您将JSON解析为可以访问
不是对OP问题的直接回答,而是受到启发,也许对某些人有用。我已经使用内部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 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" |
获得点访问(而不是数组访问)的一个简单方法是在Python中使用一个普通的对象。这样地:
1 2 3 4 | class YourObject: def __init__(self, *args, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) |
…像这样使用:
1 2 3 | >>> obj = YourObject(key="value") >>> print(obj.key) "value" |
…要将其转换为dict:
10该解决方案是对epool提供的解决方案的改进,以满足op以一致的方式访问嵌套dict的要求。epool的解决方案不允许访问嵌套的dict。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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] |
有了这门课,人们现在可以做一些类似的事情: