我如何使Python字典成员访问通过点“。”?

例如,我想写mydict.val而不是mydict['val']。

我还想以这种方式访问嵌套字典。例如

mydict.mydict2.val 

会提到

mydict = { 'mydict2': { 'val': ... } }

当前回答

我最近遇到了“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)更有效,当你有大量嵌套字典时,dotmap会产生python递归错误。

链接到图书馆和详细信息:https://pypi.org/project/python-box/

其他回答

语言本身不支持这一点,但有时这仍然是一个有用的需求。除了Bunch recipe,你还可以写一个小方法,可以使用虚线字符串访问字典:

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提供的解决方案的改进,以满足OP以一致的方式访问嵌套字典的需求。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.

不是对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"

使用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'])

用于无限级别的字典、列表、字典的列表和列表的字典的嵌套。

它还支持酸洗

这是这个答案的延伸。

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()