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

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

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


当前回答

一般情况下它不成立。不是所有有效的dict键都有可寻址的属性(“键”)。所以,你要小心。

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

其他回答

这不是一个“好”的答案,但我认为这是俏皮的(它不处理嵌套字典在当前形式)。简单地将dict包装在函数中:

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

现在你的语法略有不同。访问dict项就像访问属性f.key一样。要以通常的方式访问dict项(和其他dict方法),请执行f()['key'],我们可以通过使用关键字参数和/或字典调用f来方便地更新dict

例子

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

就是这样。如果有人提出这种方法的优点和缺点,我会很高兴。

你可以用我刚做的这个类来做。对于这个类,您可以像使用另一个字典(包括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!'
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']

很抱歉再添加一个,但这一个解决了subdicts和纠正AttributeError,尽管非常简单:

class DotDict(dict):
    def __init__(self, d: dict = {}):
        super().__init__()
        for key, value in d.items():
            self[key] = DotDict(value) if type(value) is dict else value
    
    def __getattr__(self, key):
        if key in self:
            return self[key]
        raise AttributeError(key) #Set proper exception, not KeyError

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

为了给答案增加一些变化,sci-kit learn将其实现为一串:

class Bunch(dict):                                                              
    """ Scikit Learn's container object                                         

    Dictionary-like object that exposes its keys as attributes.                 
    >>> b = Bunch(a=1, b=2)                                                     
    >>> b['b']                                                                  
    2                                                                           
    >>> b.b                                                                     
    2                                                                           
    >>> b.c = 6                                                                 
    >>> b['c']                                                                  
    6                                                                           
    """                                                                         

    def __init__(self, **kwargs):                                               
        super(Bunch, self).__init__(kwargs)                                     

    def __setattr__(self, key, value):                                          
        self[key] = value                                                       

    def __dir__(self):                                                          
        return self.keys()                                                      

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

    def __setstate__(self, state):                                              
        pass                       

您所需要的是获取setattr和getattr方法—getattr检查字典键,然后继续检查实际属性。setstaet是针对pickle /unpickling“bunch”的修复-如果感兴趣,请检查https://github.com/scikit-learn/scikit-learn/issues/6196

让我发布另一个实现,它基于Kinvais的答案,但集成了http://databio.org/posts/python_AttributeDict.html中提出的AttributeDict的思想。

这个版本的优点是它也适用于嵌套字典:

class AttrDict(dict):
    """
    A class to convert a nested Dictionary into an object with key-values
    that are accessible using attribute notation (AttrDict.attribute) instead of
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
    """

    # Inspired by:
    # http://stackoverflow.com/a/14620633/1551810
    # http://databio.org/posts/python_AttributeDict.html

    def __init__(self, iterable, **kwargs):
        super(AttrDict, self).__init__(iterable, **kwargs)
        for key, value in iterable.items():
            if isinstance(value, dict):
                self.__dict__[key] = AttrDict(value)
            else:
                self.__dict__[key] = value