Python 像属性一样访问字典键?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4984647/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 18:27:56  来源:igfitidea点击:

Accessing dict keys like an attribute?

pythondictionarysyntaxattributes

提问by Izz ad-Din Ruhulessin

I find it more convenient to access dict keys as obj.fooinstead of obj['foo'], so I wrote this snippet:

我发现访问 dict 键更方便obj.foo而不是obj['foo'],所以我写了这个片段:

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

However, I assume that there must be some reason that Python doesn't provide this functionality out of the box. What would be the caveats and pitfalls of accessing dict keys in this manner?

但是,我认为 Python 没有提供这种现成的功能肯定是有原因的。以这种方式访问​​ dict 键的注意事项和陷阱是什么?

回答by Hery

You can have all legal string characters as part of the key if you use array notation. For example, obj['!#$%^&*()_']

如果使用数组表示法,则可以将所有合法的字符串字符作为键的一部分。例如,obj['!#$%^&*()_']

回答by The Communist Duck

What if you wanted a key which was a method, such as __eq__or __getattr__?

如果您想要一个作为方法的键,例如__eq__or__getattr__怎么办?

And you wouldn't be able to have an entry that didn't start with a letter, so using 0343853as a key is out.

而且您将无法获得不以字母开头的条目,因此0343853用作键已失效。

And what if you didn't want to use a string?

如果您不想使用字符串怎么办?

回答by tallseth

It doesn't work in generality. Not all valid dict keys make addressable attributes ("the key"). So, you'll need to be careful.

它在一般情况下不起作用。并非所有有效的字典键都具有可寻址属性(“键”)。所以,你需要小心。

Python objects are all basically dictionaries. So I doubt there is much performance or other penalty.

Python 对象基本上都是字典。所以我怀疑有很多表现或其他惩罚。

回答by David W

No need to write your own as setattr()and getattr() already exist.

无需编写自己的,因为 setattr()和 getattr() 已经存在。

The advantage of class objects probably comes into play in class definition and inheritance.

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

回答by Senthil Kumaran

tuples can be used dict keys. How would you access tuple in your construct?

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

Also, namedtupleis a convenient structure which can provide values via the attribute access.

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

回答by slacy

From This other SO questionthere's a great implementation example that simplifies your existing code. How about:

This other SO question有一个很好的实现示例,可以简化您现有的代码。怎么样:

class AttributeDict(dict): 
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

Much more concise and doesn't leave any room for extra cruft getting into your __getattr__and __setattr__functions in the future.

更简洁,不留任何余地额外的克鲁夫特进入你__getattr____setattr__功能的未来。

回答by Ryan

Caveat emptor: For some reasons classes like this seem to break the multiprocessing package. I just struggled with this bug for awhile before finding this SO: Finding exception in python multiprocessing

警告清空者:出于某些原因,像这样的类似乎破坏了多处理包。在找到这个 SO 之前,我只是在这个错误上挣扎了一段时间: 在 python 多处理中查找异常

回答by Kimvais

The best way to do this is:

最好的方法是:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

Some pros:

一些优点:

  • It actually works!
  • No dictionary class methods are shadowed (e.g. .keys()work just fine. Unless - of course - you assign some value to them, see below)
  • Attributes and items are always in sync
  • Trying to access non-existent key as an attribute correctly raises AttributeErrorinstead of KeyError
  • 它确实有效!
  • 没有隐藏字典类方法(例如.keys()工作得很好。除非 - 当然 - 你为它们分配一些值,见下文)
  • 属性和项目始终同步
  • 尝试访问不存在的键作为属性正确引发AttributeError而不是KeyError

Cons:

缺点:

  • Methods like .keys()will notwork just fine if they get overwritten by incoming data
  • Causes a memory leakin Python < 2.7.4 / Python3 < 3.2.3
  • Pylint goes bananas with E1123(unexpected-keyword-arg)and E1103(maybe-no-member)
  • For the uninitiated it seems like pure magic.
  • 如果它们被传入的数据覆盖,像这样的方法.keys()无法正常工作
  • 在 Python < 2.7.4 / Python3 < 3.2.3 中导致内存泄漏
  • Pylint 去香蕉E1123(unexpected-keyword-arg)E1103(maybe-no-member)
  • 对于外行来说,这似乎是纯粹的魔法。

A short explanation on how this works

关于其工作原理的简短说明

  • All python objects internally store their attributes in a dictionary that is named __dict__.
  • There is no requirement that the internal dictionary __dict__would need to be "just a plain dict", so we can assign any subclass of dict()to the internal dictionary.
  • In our case we simply assign the AttrDict()instance we are instantiating (as we are in __init__).
  • By calling super()'s __init__()method we made sure that it (already) behaves exactly like a dictionary, since that function calls all the dictionary instantiationcode.
  • 所有 python 对象在内部将它们的属性存储在一个名为 的字典中__dict__
  • 没有要求内部字典__dict__需要“只是一个普通的字典”,因此我们可以将 的任何子类分配给dict()内部字典。
  • 在我们的例子中,我们只是简单地分配AttrDict()我们正在实例化的实例(就像我们在 中一样__init__)。
  • 通过调用super()__init__()方法,我们确保它(已经)表现得与字典完全一样,因为该函数调用了所有字典实例化代码。

One reason why Python doesn't provide this functionality out of the box

Python 不开箱即用地提供此功能的一个原因

As noted in the "cons" list, this combines the namespace of stored keys (which may come from arbitrary and/or untrusted data!) with the namespace of builtin dict method attributes. For example:

正如“缺点”列表中所指出的,这将存储键的命名空间(可能来自任意和/或不受信任的数据!)与内置 dict 方法属性的命名空间相结合。例如:

d = AttrDict()
d.update({'items':["Hymanet", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

回答by Rafe

I created this based on the input from this thread. I need to use odict though, so I had to override get and set attr. I think this should work for the majority of special uses.

我根据这个线程的输入创建了这个。不过我需要使用 odict,所以我不得不覆盖 get 和 set attr。我认为这应该适用于大多数特殊用途。

Usage looks like this:

用法如下所示:

# Create an ordered dict normally...
>>> od = OrderedAttrDict()
>>> od["a"] = 1
>>> od["b"] = 2
>>> od
OrderedAttrDict([('a', 1), ('b', 2)])

# Get and set data using attribute access...
>>> od.a
1
>>> od.b = 20
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])

# Setting a NEW attribute only creates it on the instance, not the dict...
>>> od.c = 8
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])
>>> od.c
8

The class:

班上:

class OrderedAttrDict(odict.OrderedDict):
    """
    Constructs an odict.OrderedDict with attribute access to data.

    Setting a NEW attribute only creates it on the instance, not the dict.
    Setting an attribute that is a key in the data will set the dict data but 
    will not create a new instance attribute
    """
    def __getattr__(self, attr):
        """
        Try to get the data. If attr is not a key, fall-back and get the attr
        """
        if self.has_key(attr):
            return super(OrderedAttrDict, self).__getitem__(attr)
        else:
            return super(OrderedAttrDict, self).__getattr__(attr)


    def __setattr__(self, attr, value):
        """
        Try to set the data. If attr is not a key, fall-back and set the attr
        """
        if self.has_key(attr):
            super(OrderedAttrDict, self).__setitem__(attr, value)
        else:
            super(OrderedAttrDict, self).__setattr__(attr, value)

This is a pretty cool pattern already mentioned in the thread, but if you just want to take a dict and convert it to an object that works with auto-complete in an IDE, etc:

这是线程中已经提到的一个非常酷的模式,但是如果您只想获取一个 dict 并将其转换为一个可以在 IDE 中使用自动完成功能的对象,等等:

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d

回答by lindyblackburn

You can pull a convenient container class from the standard library:

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

from argparse import Namespace

to avoid having to copy around code bits. No standard dictionary access, but easy to get one back if you really want it. The code in argparse is simple,

以避免必须复制代码位。没有标准的字典访问权限,但如果您真的需要,很容易找回。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__