python使用self.__dict__的getattr和setattr
我一直在研究如何重写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) 属性的操作实际上是转发的,也就是说有效的属性在事先并不知道,比如一个表示数据库行的类,用户可以把列当作属性来操作。我需要在运行时检查给定的属性名是否与列名匹配,也许我还想忽略大小写的差异等等。
还有一点,有时候使用包含关系比继承更好。你可以创建一个包含字典的类,而不是直接从字典继承。