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

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

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


当前回答

编辑:NeoBunch是废弃的,Munch(上面提到过)可以作为一个替代品。不过,我把这个解决方案留在这里,它可能对某些人有用。

正如Doug所指出的,有一个Bunch包,你可以使用它来实现obj。关键功能。实际上有一个更新的版本叫做

尼奥邦克·蒙克

它有一个伟大的功能,通过neobunchify函数将你的字典转换为NeoBunch对象。我经常使用Mako模板,将数据作为NeoBunch对象传递使它们更具可读性,所以如果你碰巧在你的Python程序中使用了一个普通的字典,但想要在Mako模板中使用点符号,你可以这样使用:

from mako.template import Template
from neobunch import neobunchify

mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
    out_file.write(mako_template.render(**neobunchify(data)))

Mako模板看起来像这样:

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor

其他回答

买者自负:出于某些原因,这样的类似乎会破坏多处理包。我只是在发现这个bug之前挣扎了一段时间,所以: 在python multiprocessing中查找异常

没有必要自己写 Setattr()和getattr()已经存在。

类对象的优势可能在类定义和继承中发挥作用。

以这种方式访问字典键的注意事项和缺陷是什么?

正如@Henry所指出的,在dict中不能使用点访问的一个原因是,它将dict键名限制为python有效变量,从而限制了所有可能的名称。

下面是一些例子,说明为什么在给定字典d的情况下,点点访问通常没有帮助:

有效性

以下属性在Python中是无效的:

d.1_foo                           # enumerated names
d./bar                            # path names
d.21.7, d.12:30                   # decimals, time
d.""                              # empty strings
d.john doe, d.denny's             # spaces, misc punctuation 
d.3 * x                           # expressions  

风格

PEP8约定将对属性命名施加软约束:

A.保留关键字(或内置函数)名称:

d.in
d.False, d.True
d.max, d.min
d.sum
d.id

如果函数参数的名称与保留关键字冲突,通常最好在后面添加一个下划线…

B.方法和变量名的大小写规则:

变量名遵循与函数名相同的约定。

d.Firstname
d.Country

使用函数命名规则:小写字母,单词之间用下划线分隔,以提高可读性。


有时,在pandas这样的库中会出现这些问题,这些库允许按名称点访问DataFrame列。解决命名限制的默认机制也是数组表示法——括号中的字符串。

如果这些约束不适用于您的用例,那么在点访问数据结构上有几个选项。

我发现自己想知道python生态系统中“字典键作为attr”的当前状态。正如一些评论者所指出的,这可能不是你想要从头开始的东西,因为有几个陷阱和脚枪,其中一些非常微妙。此外,我不建议使用Namespace作为基类,我已经走上了那条路,它并不漂亮。

幸运的是,有几个开源包提供了这个功能,可以安装了!不幸的是,有几个包。以下是截至2019年12月的概要。

竞争者(最近提交到|#提交|#投稿|覆盖率%):

上瘾者(2021-01-05 | 229 | | 100%)22 蒙克(2021-01-22 | 166 | 17 | ?) easydict (2021-02-28 | 54 | 7% | ?) attrdict(| 108 | 5 |地址:100%) prodict (2021-03-06 | 100 | 2 | ?)

不再保养或保养不足:

treedict (2014-03-28 | 95 | 2 | ?) bunch (2012-03-12 | 20% | 2 | ?) NeoBunch

目前我推荐咀嚼或上瘾。它们拥有最多的提交、贡献者和发布,这意味着它们都有一个健康的开源代码库。他们有最干净的自述。Md, 100%的覆盖率,以及一组好看的测试。

我在这场比赛中没有一只狗(现在!),除了滚动我自己的dict/attr代码,浪费了大量的时间,因为我不知道所有这些选项:)。我可能会在未来贡献给addict/munch,因为我宁愿看到一个完整的包,而不是一堆碎片化的包。如果你喜欢它们,就投稿吧!特别是,看起来munch可以使用codecov徽章,addict可以使用python版本徽章。

瘾君子优点:

递归初始化(foo.a.b.c = 'bar'),类字典参数成为成瘾。Dict

成瘾的缺点:

阴影打字。词典,如果你从成瘾进口词典 不检查密钥。由于允许递归init,如果你拼错了一个键,你只是创建一个新属性,而不是KeyError(感谢AljoSt)

蒙克优点:

独特的命名 内置的JSON和YAML的ser/de函数

蒙克缺点:

没有递归初始化(你不能构造foo.a.b.c = 'bar',你必须设置foo.a.b.c = 'bar')。A,然后foo, A。b等。

其中我发表评论

Many moons ago, when I used text editors to write python, on projects with only myself or one other dev, I liked the style of dict-attrs, the ability to insert keys by just declaring foo.bar.spam = eggs. Now I work on teams, and use an IDE for everything, and I have drifted away from these sorts of data structures and dynamic typing in general, in favor of static analysis, functional techniques and type hints. I've started experimenting with this technique, subclassing Pstruct with objects of my own design:

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))


class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

This gives you an object which still behaves like a dict, but also lets you access keys like attributes, in a much more rigid fashion. The advantage here is I (or the hapless consumers of your code) know exactly what fields can and can't exist, and the IDE can autocomplete fields. Also subclassing vanilla dict means json serialization is easy. I think the next evolution in this idea would be a custom protobuf generator which emits these interfaces, and a nice knock-on is you get cross-language data structures and IPC via gRPC for nearly free.

如果您决定使用attrt -dicts,那么为了您自己(和您的队友)的理智,有必要记录期望哪些字段。

请随意编辑/更新这篇文章,以保持它的最新!

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

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__