我发现它更方便访问字典键作为obj。foo而不是obj['foo'],所以我写了这个片段:

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

然而,我认为一定有一些原因,Python没有提供开箱即用的功能。以这种方式访问字典键的注意事项和缺陷是什么?


当前回答

你可以从标准库中获取一个方便的容器类:

from argparse import Namespace

避免复制代码位。没有标准的字典访问,但如果你真的想要的话,很容易得到一个。argparse中的代码很简单,

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

    def __contains__(self, key):
        return key in self.__dict__

其他回答

其中我回答了被问到的问题

为什么Python不开箱即用呢?

我怀疑这与Python的禅意有关:“应该有一种——最好只有一种——明显的方法来做到这一点。”这将创建两种明显的方法来访问字典中的值:obj['key']和obj.key。

注意事项和陷阱

这包括代码中可能缺乏清晰性和混乱。也就是说,下面的内容可能会让以后要维护您的代码的人感到困惑,如果您暂时不回去的话,甚至会让您感到困惑。禅宗说:“可读性很重要!”

>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1

如果d被实例化,或者KEY被定义,或者d[KEY]被赋值的位置远离d.s spam的使用位置,那么很容易导致对正在执行的操作的混淆,因为这不是一个常用的习惯用法。我知道这可能会让我感到困惑。

另外,如果你像下面这样改变KEY的值(但是没有改变d.s rspam),你现在得到:

>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'

在我看来,不值得这么努力。

其他物品

正如其他人所注意到的,您可以使用任何可哈希对象(不仅仅是字符串)作为dict键。例如,

>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>> 

是合法的,但是

>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
  File "<stdin>", line 1
  d.(2, 3)
    ^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>> 

不是。这使您可以访问字典键的整个范围的可打印字符或其他可哈希对象,而在访问对象属性时则没有这些权限。这使得缓存对象元类这样的魔法成为可能,就像Python Cookbook(第9章)中的食谱一样。

其中我发表评论

我更喜欢垃圾邮件的美感。eggs over spam['eggs'](我认为它看起来更干净),当我遇到namedtuple时,我真的开始渴望这个功能。但是能够做以下事情的便利性胜过它。

>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>

这是一个简单的例子,但我经常发现自己在不同的情况下使用字典,而不是使用obj。键符号(即,当我需要从XML文件中读取prefs时)。在其他情况下,当我想实例化一个动态类并为其添加一些属性时,我继续使用字典来保持一致性,以增强可读性。

我相信OP早就解决了这个问题,让他满意了,但如果他仍然想要这个功能,那么我建议他从pypi下载一个提供该功能的包:

邦奇是我更熟悉的人。dict的子类,所以你有所有的功能。 AttrDict看起来也很不错,但我对它不熟悉,也没有像我对Bunch那样详细地查看源代码。 上瘾是积极维护,并提供attrlike访问和更多。 正如Rotareti在评论中提到的,Bunch已经被弃用了,但是有一个活跃的分支叫做Munch。

但是,为了提高代码的可读性,我强烈建议他不要混合使用他的符号风格。如果他喜欢这种表示法,那么他应该简单地实例化一个动态对象,添加他想要的属性,然后收工:

>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}

其中我更新,在评论中回答一个后续问题

在下面的评论中,Elmo问道:

如果你想再深入一点呢?(指类型(…))

虽然我从未使用过这个用例(同样,我倾向于使用嵌套的dict,对于 一致性),下面的代码工作:

>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
...     setattr(d, x, C())
...     i = 1
...     for y in 'one two three'.split():
...         setattr(getattr(d, x), y, i)
...         i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}

最简单的方法是定义一个类,我们称之为Namespace。在字典上使用对象dict.update()。然后,字典将被视为一个对象。

class Namespace(object):
    '''
    helps referencing object in a dictionary as dict.key instead of dict['key']
    '''
    def __init__(self, adict):
        self.__dict__.update(adict)



Person = Namespace({'name': 'ahmed',
                     'age': 30}) #--> added for edge_cls


print(Person.name)

你可以从标准库中获取一个方便的容器类:

from argparse import Namespace

避免复制代码位。没有标准的字典访问,但如果你真的想要的话,很容易得到一个。argparse中的代码很简单,

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

    def __contains__(self, key):
        return key in self.__dict__

使用SimpleNamespace:

from types import SimpleNamespace

obj = SimpleNamespace(color="blue", year=2050)

print(obj.color) #> "blue"
print(obj.year) #> 2050

编辑/更新:对OP的问题的更近的答案,从字典开始:

from types import SimpleNamespace

params = {"color":"blue", "year":2020}

obj = SimpleNamespace(**params)

print(obj.color) #> "blue"
print(obj.year) #> 2050

元组可以使用字典键。如何在构造中访问元组?

另外,namedtuple是一种方便的结构,可以通过属性访问提供值。