关于python:如何使用点“。”来访问字典成员?

How to use a dot “.” to access members of dictionary?

如何让Python字典的成员accessible点离开。""什么?P></

mydict['val']instead of for example,我喜欢写作,写mydict.val。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序列化)一样使用Map对象,也可以使用点表示法。我希望能帮助你:

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']


通过pip安装dotmap

1
pip install dotmap

它做你想做的所有事情,并将dict子类化,因此它像一个普通字典一样工作:

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'

除此之外,您还可以将它与dict对象进行转换:

1
2
d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

这意味着,如果您想要访问的内容已经以dict形式存在,您可以将其转换为dotmap以方便访问:

1
2
3
4
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

最后,它会自动创建新的子dotmap实例,这样您就可以执行以下操作:

1
2
m = DotMap()
m.people.steve.age = 31

与束比较

完全公开:我是dotmap的创建者。我创建它是因为Bunch缺少这些特性

  • 记住添加的顺序项并按该顺序迭代
  • 自动创建子级dotmap,这节省了时间,并且在您有很多层次结构的情况下使代码更清晰。
  • dict构造并递归地将所有子dict实例转换为dotmap


从dict派生并实现__getattr____setattr__

或者你可以用一束非常相似的。

我认为不可能用monkeypatch内置的dict类。


我试过这个:

1
2
3
class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

你也可以试试__getattribute__

使每个dict都成为dotdict类型就足够了,如果您想从多层dict初始化它,也可以尝试实现__init__


Fabric有一个非常好的、最小的实现。扩展到允许嵌套访问,我们可以使用defaultdict,结果如下:

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

这就阐述了库格尔的回答"从口述中派生并实现__getattr____setattr__"。现在你知道怎么做了!


如果您想对修改过的字典进行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中是分开的,您不应该希望它们执行相同的操作。如果你有一些应该具有可访问属性的东西,那么就创建一个类(可能是由namedtuple生成的类),并使用[]符号从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
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)

如果有人决定永久性地将dict转换为反对,应该这样做。您可以在访问之前创建一个一次性对象。

1
d = dict_to_object(d)


为了建立在epool答案的基础上,此版本允许您通过点运算符访问内部的任何dict:

1
2
3
4
5
foo = {
   "bar" : {
       "baz" : [ {"boo" :"hoo"} , {"baba" :"loo"} ]
    }
}

例如,foo.bar.baz[1].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]

使用__getattr__,非常简单,可在Python 3.4.3

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解析为可以访问with.attribute.lookups.like.this.r()的内容,主要是因为在开始处理之前,我没有看到这个答案。


不是对OP问题的直接回答,而是受到启发,也许对某些人有用。我已经使用内部__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
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]

有了这门课,人们现在可以做一些类似的事情:A.B.C.D