python使用self.__dict__的getattr和setattr

6 投票
3 回答
2138 浏览
提问于 2025-04-17 20:43

我一直在研究如何重写getattr和setattr这两个方法,但我搞不清楚如果在构造函数里用self.__dict__ = self,是否还需要重写这两个方法。

一旦我创建了这个类的实例

a = OPT(foo='bar')

使用a.foo和a['foo']时,无论有没有__getattr__和__setattr__的声明,效果都是一样的。

有人能解释一下我是否需要同时使用这两个方法吗?如果需要,为什么呢?谢谢!

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

    def __init__(self, *args, **kwargs):
        super(OPT, self).__init__(*args, **kwargs)
        self.__dict__ = self

3 个回答

0

这是对Marquinho Peli回答的一个更详细的改进版本。主要的变化是支持包含嵌套列表的字典。

class DotDict(dict):
    """A dictionary that's recursively navigable with dots, not brackets."""

    def __init__(self, data: dict = None):
        super().__init__()
        if data is None or not isinstance(data, dict):
            raise AttributeError(f"{type(self).__name__} must be instantiated with a dictionary, not a {type(data).__name__}.")
        for key, value in data.items():
            if isinstance(value, list):
                self[key] = [DotDict(item) for item in value]
            elif isinstance(value, dict):
                self[key] = DotDict(value)
            else:
                self[key] = value

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(f"attribute .{key} not found")

使用示例:

d = {'key1': 'value1',
     'key2': 'value2',
     'key3': {'key3a': 'value3a'},
     'key4': {'key4a': [{'key4aa': 'value4aa',
                         'key4ab': 'value4ab',
                         'key4ac': 'value4ac'}],
              'key4b': 'value4b'}}

dd = DotDict(d)
print(dd.key4.key4a[0].key4aa)  # value4aa
dd.key4.key4a[0].key4aa = 'newval'
print(dd.key4.key4a[0].key4aa)  # newval

print(dd.key4.key4a[0].key4aaa) # AttributeError: attribute .key4aaa not found

DotDict({}) # OK
DotDict()   # AttributeError: DotDict must be instantiated with dictionary, not a NoneType.
0

试试这个:

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__
2

当你想在类的用户获取或设置属性时做一些额外的事情,就可以重写getattr和setattr这两个方法。例如:

1) 你可能希望在用户操作一个无效属性时,不抛出异常,而是返回None,这样对于未知的属性就不会出错。

2) 属性的操作实际上是转发的,也就是说有效的属性在事先并不知道,比如一个表示数据库行的类,用户可以把列当作属性来操作。我需要在运行时检查给定的属性名是否与列名匹配,也许我还想忽略大小写的差异等等。

还有一点,有时候使用包含关系比继承更好。你可以创建一个包含字典的类,而不是直接从字典继承。

撰写回答