Accessing dict keys like an attribute?

2018-12-31 10:01发布

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

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

However, I assume 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?

23条回答
裙下三千臣
2楼-- · 2018-12-31 10:32

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,

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__
查看更多
琉璃瓶的回忆
3楼-- · 2018-12-31 10:33

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

查看更多
伤终究还是伤i
4楼-- · 2018-12-31 10:35

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

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.

查看更多
旧时光的记忆
5楼-- · 2018-12-31 10:35

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.

查看更多
荒废的爱情
6楼-- · 2018-12-31 10:35

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.

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:

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d
查看更多
ら面具成の殇う
7楼-- · 2018-12-31 10:36

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

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

查看更多
登录 后发表回答