所以,假设我想编写自己的类,重写__getattribute__
函数。每当有人调用未定义的属性时,我希望它生成一个随机的int。
X = GetAttribute()
print(X.predefined_attribute) # "First attribute"
X.attr2 = "Hi"
print(X.attr2) # "Hi"
print(X.attr3) # random int
显然,我不能写这样的东西,因为它会导致递归。
class GetAttribute(object):
def __init__(self):
self.predefined_attribute = "First attribute"
def __getattribute__(self, attr):
if attr not in self.__dict__: # the bad line
return randint(0, 9999)
else:
return object.__getattribute__(self, attr)
如何在不使用__dict__
的情况下获取有关已定义属性的信息?
答案 0 :(得分:2)
我强烈建议您重新考虑覆盖__getattribute__
并改为使用object.__getattr__()
hook。对于任何缺少的属性,系统会自动调用该方法,并且不会干扰dir()
或__dict__
内省:
class GetAttribute(object):
def __init__(self):
self.predefined_attribute = "First attribute"
def __getattr__(self, attr):
# self.__dict__ can be used here but is not needed for your
# sample usecase.
return randint(0, 9999)
您自己的实现存在缺陷,因为您未能检查属性的类。 __dict__
是类的描述符,尝试访问self.__dict__
也由object.__getattribute__
处理,触发无限递归。您可以完全使用object.__getattribute__
优先来避免此问题。您可以捕获可能引发的AttributeError
异常:
def __getattribute__(self, attr):
try:
return object.__getattribute__(self, attr)
except AttributeError:
return randint(0, 9999)
在测试之前,重新实施descriptor protocol以检索__dict__
属性的路径更为痛苦:
def __getattribute__(self, attr):
cls = type(self)
# retrieve the __dict__ descriptor, and bind it to the instance
__dict__ = cls.__dict__['__dict__'].__get__(self)
# test against the instance dictionary and all classes in the MRO
if attr not in __dict__ and not any(attr in c.__dict__ for c in cls.__mro__):
return randint(0, 9999)
return object.__getattribute__(self, attr)
或者您可以通过self.__dict__
访问object.__getattribute__(self, '__dict__')
。您还必须测试MRO类,因为它们也为您的实例提供了属性;你不希望X.__class__
返回一个随机整数而不是GetAttribute
本身。
但是,实现__getattr__
已经涵盖了这个用例,这是一个更简洁,更简单的选项。
最后但并非最不重要的是,您应该使用object.__getattribute__(self, ...)
而不是使用super().__getattribute__(...)
,以确保您不会在类层次结构中跳过任何其他__getattribute__
挂钩。
答案 1 :(得分:0)
如果您需要绕过自己的__getattribute__
,例如获取“真实”self.__dict__
,则可以明确调用超类__getattribute__
:
if attr not in super().__getattribute__('__dict__'):
但是,对于您的情况,实施__getattr__
而不是__getattribute__
可能更容易。 __getattr__
仅针对__getattribute__
引发AttributeError
的属性查询进行调用:
def __getattr__(self, name):
return randint(0, 9999)