如何使用点“.”来访问字典的成员?

How to use a dot "." to access members of dictionary?

提问人:bodacydo 提问时间:3/1/2010 最后编辑:dreftymacbodacydo 更新时间:9/20/2023 访问量:307675

问:

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

例如,与其写,不如写。mydict['val']mydict.val

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

mydict.mydict2.val 

将参考

mydict = { 'mydict2': { 'val': ... } }
python 字典 语法 套嵌套属性

评论

30赞 Mike Graham 3/1/2010
人们使用嵌套字典的许多情况与带有元组作为键的字典一样好或更好,其中替换为 .d[a][b][c]d[a, b, c]
9赞 Bryan Oakley 3/1/2010
这不是魔法:foo={};foo[1,2,3] = “一,二,三!”;foo.keys() => [(1,2,3)]
15赞 bodacydo 3/1/2010
哇。哇,又来了。我不知道元组可以成为字典的键。哇,第三次了。
5赞 Larry Hastings 3/1/2010
任何“可散列”的对象都可以用作字典的键。大多数不可变对象也是可散列的,但前提是它们的所有内容都是可散列的。代码 d[1, 2, 3] 之所以有效,是因为 “,” 是 “创建一个元组运算符”;它与 d[(1, 2, 3)] 相同。括号在元组声明周围通常是可选的。
7赞 Todor Minakov 11/1/2015
你有没有考虑过钥匙本身有一个点的情况 - ?或者当键是关键字时,例如“from”?我已经考虑过几次了,它更多的是问题和故障排除,而不是感知到的好处。{"my.key":"value"}

答:

60赞 Kugel 3/1/2010 #1

派生自 dict 和 和 implement 和 。__getattr____setattr__

或者你可以使用非常相似的 Bunch

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

评论

2赞 bodacydo 3/1/2010
monkeypatch 到底是什么意思?我听说过它,但没有使用过。(对不起,我问了这样的新手问题,我还不擅长编程(我只是二年级学生。
9赞 Mike Graham 3/1/2010
Monkeypatching 是利用 Python(或任何语言)的动态性来改变通常在源代码中定义的东西。它特别适用于在创建类后更改类的定义。
0赞 JayD3e 7/23/2015
如果您经常使用此功能,请注意 Bunch 的速度。我经常使用它,最终消耗了我请求时间的三分之一。查看我的回答,以获得更详细的解释。
15赞 Mike Graham 3/1/2010 #2

不要。属性访问和索引在 Python 中是分开的,您不应该希望它们执行相同的操作。创建一个类(可能是由 ),如果你有一些东西应该具有可访问的属性,并使用符号从字典中获取一个项目。namedtuple[]

评论

2赞 Bryan Oakley 3/1/2010
我可以看到它的用例;事实上,我几周前就这样做了。就我而言,我想要一个可以使用点表示法访问属性的对象。我发现简单地从 dict 继承非常容易,所以我内置了所有 dict 功能,但这个对象的公共接口使用点表示法(它本质上是一些静态数据的只读接口)。我的用户对 'foo.bar' 比对 'foo[“bar”]' 更满意,我很高兴我可以利用 dict 数据类型的功能。
12赞 Larry Hastings 3/1/2010
你已经知道好的 Python 风格:我们告诉你,不要假装 dict 的值是属性。这是不好的做法。例如,如果要存储与字典的现有属性同名的值,例如“items”或“get”或“pop”,该怎么办?可能是一些令人困惑的事情。所以不要这样做!
6赞 bodacydo 3/1/2010
哎呀,我忘记了“items”、“get”或“pop”等属性。感谢您提出这个重要的例子!
5赞 bgusach 11/21/2014
@Gabe,好久不见了......但我认为值得一说。它“在 JS 中不够好”:它“在 JS 中足够可怕”。当您存储与原型链中其他重要属性同名的密钥/属性时,它会变得很有趣。
3赞 logicOnAbstractions 12/29/2021
我不同意这里发表的相当教条的声明。虽然这通常是正确的,但 .符号更清晰,更易于阅读。我可以看到很多用例,其中它导致问题的可能性非常小。
6赞 pbanka 9/24/2011 #3

语言本身不支持这一点,但有时这仍然是一个有用的要求。除了 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'
>>

评论

1赞 Ranel Padon 2/20/2022
这与我最初的计划相似。简单、整洁、无依赖。也适用于嵌套案例。我修改了最后一行,但返回而不是与不存在的键保持一致:。干杯!None{}dict.get()return current_data or None
0赞 Jonny Blaze 10/26/2022
我根据我的情况调整了您的答案,并使用了管道,因为有些键有点,有时它们是列表 def _get_value_by_pipe_notation(path: str, search: dict): current = search for chunk in path.split('|'): if isinstance(current, dict): current = current.get(chunk, {}) elif isinstance(current, list): current = current[int(chunk)] else: return “#error#” if isinstance(current, (int, float)): 返回当前 elif len(current): 返回当前 else: return “#notfound#”
24赞 tdihp 2/9/2012 #4

我试过了这个:

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

你也可以试试。__getattribute__

让每个字典都成为一种 dotdict 就足够了,如果你想从多层字典中初始化它,也尝试实现。__init__

评论

0赞 Daniel Moskovich 6/13/2018
我发现通过将其嵌入函数中很有用,方法是将其放在函数之前,然后放置' if isinstance(name, dict): return DotDict(name) return name 'def docdict(name):
0赞 D Sievers 3/7/2019
很棒的简单例子..我对此进行了一些扩展,以便嵌套字典易于链接,类似于@DanielMoskovich,但也正确返回 int、string 等的叶节点......如果未找到,则为 nullclass 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]
11赞 Michael Jackson 8/30/2012 #5

在库格尔的回答的基础上,考虑到迈克·格雷厄姆(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
507赞 derek73 5/16/2014 #6

我一直把它放在一个 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'

评论

13赞 andreas-h 2/20/2016
很简单的答案,太好了!您是否碰巧知道我需要做什么才能在 IPython 工作中完成 Tab 键?该类需要实现 __dir__(self),但不知何故我无法让它工作。
18赞 tmthyjames 6/11/2016
为简单起见,+1。但似乎不适用于嵌套字典。 引发属性错误,但工作正常。d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.bard.foo
4赞 David 7/14/2016
是的,这不适用于复杂的嵌套结构。
30赞 TMKasun 7/12/2017
@tmthyjames,您可以简单地在 getter 方法中返回 dotdict 类型对象,以递归方式访问具有点表示法的属性,例如: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__
7赞 NichtJens 9/6/2017
经过试验,这似乎确实是一个坏主意,因为它会返回而不是为丢失的项目引发错误......getNone
178赞 Chris Redford 2/12/2015 #7

安装方式dotmappip

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

这意味着,如果您要访问的内容已经形成,您可以将其转换为便于访问:dictDotMap

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

最后,它会自动创建新的子实例,因此您可以执行如下操作:DotMap

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

与 Bunch 的比较

完全披露:我是 DotMap 的创建者。我创建它是因为缺少这些功能Bunch

  • 记住添加项的顺序并按该顺序进行迭代
  • 自动创建子项,当您有很多层次结构时,可以节省时间并使代码更简洁DotMap
  • 构造 从 a 并以递归方式将所有子实例转换为dictdictDotMap

评论

3赞 dlite922 11/11/2015
:-)你能让它与名字中已经有点的键一起工作吗? 可以通过以下方式访问 那太棒了。将平面地图转换为深度地图,然后将 DotMap 应用于它需要一些回归,但这是值得的!{"test.foo": "bar"}mymap.test.foo
0赞 Dmitri 2/14/2016
整洁。有什么方法可以使选项卡列表/完成与Jupyter笔记本中的键一起工作吗?点式访问对于交互式使用最有价值。
0赞 Chris Redford 2/14/2016
@Dmitri 很酷的产品。以前从未听说过它,所以我不确定如何让它自动完成工作。我同意使用自动完成效果最好。我使用 Sublime Text,它会自动完成以前输入的关键字。DotMap
1赞 Simon Streicher 2/16/2018
我发现它缺少对 or 之类的字典提取。事实上,它悄悄地失败了,它在提取时表现得像一个空字典。**kwargsc = {**a, **b}
1赞 Chris Redford 2/16/2018
@SimonStreicher我对此进行了测试,并得到了预期的.如果你有一个被证明的损坏案例,但不适用于 ,请将你的代码提交到 GitHub 中的“问题”选项卡。m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));2 3dict()DotMap()
3赞 JayD3e 7/23/2015 #8

我最终尝试了 AttrDictBunch 库,发现它们对我的使用来说很慢。在我和一位朋友研究之后,我们发现编写这些库的主要方法导致库通过嵌套对象积极递归,并在整个过程中复制字典对象。考虑到这一点,我们进行了两个关键的更改。1)我们使属性延迟加载 2)我们不是创建字典对象的副本,而是创建轻量级代理对象的副本。这是最终实现。使用此代码的性能提升令人难以置信。使用 AttrDict 或 Bunch 时,仅这两个库就分别消耗了我请求时间的 1/2 和 1/3(什么!?此代码将该时间减少到几乎为零(在 0.5 毫秒的范围内)。这当然取决于您的需求,但是如果您在代码中经常使用此功能,请务必使用这样简单的东西。

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 对象上编写这些内容。

202赞 epool 8/20/2015 #9

你可以用我刚刚做的这个类来做。使用此类,您可以像使用其他字典(包括 json 序列化)或点表示法一样使用该对象。希望能帮到你:Map

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

评论

36赞 berto 7/15/2016
为了在 Python 3 上工作,我更新了.iteritems().items()
15赞 mic_e 8/1/2016
请注意,这与常见预期的行为不同,因为如果属性不存在,则不会引发。相反,它将返回 .AttributeErrorNone
0赞 9/27/2017
建议添加 getstatesetstate,以便深拷贝和其他系统可以支持它。
6赞 Jens Munk 11/6/2017
您可以将构造函数简化为 。此外,您还可以添加 .然后,您可以使用点表示法添加缺失的条目。如果您希望它是可选择的,您可以添加和self.update(*args,**kwargs)__missing__(self,key): value=self[key]= type(self)(); return value__getstate____setstate__
1赞 Xiao 11/7/2018
这将使__getattr__'hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding
3赞 deepak 11/24/2015 #10

此解决方案是对 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

评论

0赞 logicOnAbstractions 12/29/2021
将 .items() 用于 python3
0赞 K.A 5/5/2022
请注意:为了在 Python 3 上工作,我将 .iteritems() 更新为 .items()
3赞 nehem 2/1/2016 #11
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)

评论

0赞 throws_exceptions_at_you 2/1/2019
def attr(**kwargs): o = lambda: 无 o.__dict__.update(**kwargs) return o
15赞 volodymyr 5/1/2016 #12

如果你想腌制修改后的字典,你需要在上面的答案中添加一些状态方法:

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

评论

0赞 Shagru 8/24/2016
感谢您对腌制的评论。我被这个错误逼疯了,才意识到这是因为这个问题!
0赞 9/27/2017
使用 copy.deepcopy 时也会发生这种情况。需要添加此内容。
0赞 martineau 1/9/2020
简单化:__getattr__ = dict.get
0赞 Daniel 8/22/2023
在 Python 3.10 中,我收到以下有关酸洗的错误:PicklingError: Can't pickle <class '__main__.dot_dict'>: attribute lookup dot_dict on __main__ failed
28赞 Dave 12/22/2016 #13

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

这在一定程度上阐述了 Kugel 的回答,即“从 dict 中派生并实现 __getattr____setattr__”。现在你知道怎么做了!

评论

3赞 miku 5/15/2020
很高兴包含一个 defaultdict - 但是这似乎只在从头开始 dict 时才有效。如果我们需要递归地将现有字典转换为“dotdict”。这里有一个允许递归转换现有对象的替代方案:gist.github.com/miku/...dotdictdict
0赞 MatrixTheatrics 11/22/2020
我非常害怕使用这个类,因为它真的不遵守 Liskov 替换原则。但这不是主要问题,可能会出现许多问题,主要围绕意外属性创建,其中一些可以通过更复杂的实现来解决,而有些则不能。您的存储库中更新的实现完全不同,但不是在为幼稚的人布置雷区方面。
0赞 Hedde van der Heide 1/31/2017 #14

不是对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"
13赞 touch my body 5/31/2017 #15

为了建立在 epool 的答案之上,这个版本允许你通过点运算符访问里面的任何字典:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "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]

评论

2赞 sasawatc 1/22/2020
Python 3:替换为 和iteritems()items()xrange()range()
1赞 Eli Burke 2/24/2021
这是对epool解决方案和deepak更新的重大更新。没有外部依赖关系,效果很好。希望它能冒泡到顶部,为人们节省一些侦查工作。
0赞 Gulzar 3/9/2022
另一个版本适用于更多类型
7赞 Senthil 11/6/2017 #16

我喜欢 Munch,它在点访问之上提供了很多方便的选项。

进口蒙克

temp_1 = {'person': { 'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify(temp_1)

dict_munch.person.fname

1赞 Emil Stenström 5/8/2018 #17

获得点访问(但不是数组访问)的一种简单方法是在 Python 中使用普通对象。喜欢这个:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

...并像这样使用它:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

...要将其转换为字典,请执行以下操作:

>>> print(obj.__dict__)
{"key": "value"}
8赞 IRSHAD 9/19/2018 #18

使用,非常简单,适用于 Python 3.4.3 中文文档__getattr__

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

输出:

10000
StackOverflow
4赞 Yaniv K. 8/2/2019 #19

这也适用于嵌套字典,并确保稍后附加的字典的行为相同:

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__
20赞 Pradip Gupta 10/3/2019 #20

我最近遇到了“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/

1赞 marnix 11/9/2019 #21

@derek73的答案很整洁,但不能腌制,也不能(深)抄,而且会因为缺少钥匙而返回。下面的代码解决了这个问题。None

编辑:我没有看到上面的答案解决了完全相同的问题(赞成)。我把答案留在这里供参考。

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

评论

0赞 Karolius 8/3/2021
或者您可以设置__getattr__ = dict.__getitem__
71赞 Dmitry Zotikov 12/26/2019 #22

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

评论

2赞 ged 1/23/2020
这种方法效果更好。(从文件加载 JSON)
3赞 Mojimi 1/29/2020
这是否考虑了嵌套字典?
13赞 Carson 3/12/2020
不支持嵌套的 Dict. docs.python.org/3.3/library/types.html#types.SimpleNamespace
1赞 S. Stromberg 1/25/2022
很好,我有一个函数从 Argparse.parse_args() 获取输出,它返回一个命名空间而不是字典。我一直在寻找一种很好的方法来测试该功能。这是完美的。
0赞 divenex 7/20/2022
PyCharm与常规类不同,代码完成不适用于 SimpleNamespace。这对我来说是一个交易破坏者 stackoverflow.com/q/71348706/3753826
3赞 Sreeragh A R 2/1/2021 #23

使用允许点访问。namedtuple

它就像一个轻量级对象,也具有元组的属性。

它允许定义属性并使用点运算符访问它们。

from collections import namedtuple
Data = namedtuple('Data', ['key1', 'key2'])

dataObj = Data(val1, key2=val2) # can instantiate using keyword arguments and positional arguments

使用点运算符访问

dataObj.key1 # Gives val1
datObj.key2 # Gives val2

使用元组索引访问

dataObj[0] # Gives val1
dataObj[1] # Gives val2

但请记住,这是一个元组;不是字典。所以下面的代码会给出错误

dataObj['key1'] # Gives TypeError: tuple indices must be integers or slices, not str

引用:namedtuple

1赞 arod 3/27/2021 #24

我只需要使用虚线路径字符串访问字典,所以我想出了:

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
2赞 Andrea Di Iura 5/25/2021 #25

这是一个古老的问题,但我最近发现它有一个可通过键访问的实现版本,即 https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html#sklearn.utils.BunchsklearndictBunch

15赞 hardika 12/8/2021 #26

您可以使用 SimpleNamespace 实现此目的

from types import SimpleNamespace
# Assign values
args = SimpleNamespace()
args.username = 'admin'

# Retrive values
print(args.username)  # output: admin

评论

2赞 divenex 7/20/2022
德米特里·佐蒂科夫(Dmitry Zotikov)的先前答案重复 stackoverflow.com/a/59480744/3753826
0赞 hardika 7/20/2022
它是相似的,但我表示它的方式不同。Ex:1 d = [], d.append({key: value}) Ex: 2 d = [{key: value}] 输出相同,但用法不同。
0赞 Arthur Khazbs 11/7/2022
请注意,a 不是 的子类,因此如果您尝试使对象可 JSON 序列化,则此操作将不起作用SimpleNamespacedictjson.dumps
3赞 Gulzar 1/11/2022 #27

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

它还支持酸洗

这是这个答案的延伸。

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

评论

0赞 Daniel 8/21/2023
使用 Python 3.10 时,我收到以下错误:_pickle.PicklingError: Can't pickle <class '__main__.DotDict'>: attribute lookup DotDict on __main__ failed
0赞 Gulzar 8/22/2023
@Daniel,如果你在一个版本上腌制,在另一个 python 版本上解腌,你就会有问题。
0赞 Daniel 8/22/2023
不过,我只使用一个 Python 版本。
0赞 Gulzar 8/23/2023
@Daniel 无法确定没有完整代码和堆栈跟踪的问题是什么,请提出一个新问题。
2赞 Sreeragh A R 2/27/2022 #28

最简单的解决方案。

定义一个只包含 pass 语句的类。为此类创建对象并使用点表示法。

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

评论

1赞 dreftymac 6/4/2022
注意:这更像是一个框架,而不是对 OP 的完整答案,因为它明显偏离了常规 python 的常规预期行为。例如,将使用这种方法的输出与使用常规 python 字典时得到的结果进行比较。诚然,您可以通过向 my_dict 类添加方法来修复这种差异,但这意味着您已经不再使用这个“最简单的解决方案”。此外,这只是您必须更改以模仿的开始。dict()pprint.pprint(person)__repr__dict()
6赞 rv.kvetch 6/24/2022 #29

我不喜欢在(超过)10 年的火灾中添加另一个日志,但我也会查看我最近发布的 dotwiz 库 - 实际上就在今年。

这是一个相对较小的库,在基准测试中获取(访问)和设置(创建)时间方面也表现得非常好,至少与其他替代方案相比是这样。

安装方式dotwizpip

pip install dotwiz

它做你想做的一切和子类,所以它像一个普通的字典一样运行: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'

最重要的是,您可以将其转换为对象或从对象转换:dict

d = dw.to_dict()
dw = DotWiz(d) # automatic conversion in constructor

这意味着,如果您要访问的内容已经形成,您可以将其转换为便于访问:dictDotWiz

import json
json_dict = json.loads(text)
data = DotWiz(json_dict)
print data.location.city

最后,我正在做的一件令人兴奋的事情是现有的功能请求,以便它自动创建新的子实例,以便您可以执行如下操作:DotWiz

dw = DotWiz()
dw['people.steve.age'] = 31

dw
# ✫(people=✫(steve=✫(age=31)))

dotmap

我在下面添加了与 dotmap 的快速和肮脏的性能比较。

首先,使用以下命令安装这两个库:pip

pip install dotwiz dotmap

出于基准测试目的,我想出了以下代码:

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

结果,在我的 M1 Mac 上运行 Python 3.10:

dotwiz (create):   0.189
dotmap (create):   1.085
dotwiz (get):   0.014
dotmap (get):   0.335
1赞 James McGuigan 7/11/2022 #30

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

这对于在 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

评论

1赞 rv.kvetch 9/20/2022
这是一个简短而不错的实现,开箱即用,可通过属性 (DOT) 访问轻松访问 Dict 键。但是,如果性能是一个问题,请注意,自定义实现(例如,请参阅我上面的帖子)在构造点访问对象时要快 ~2 倍dotwiz