在没有__dict__或dir()的情况下获取有关已定义属性的信息

时间:2017-05-31 16:57:06

标签: python class

所以,假设我想编写自己的类,重写__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__的情况下获取有关已定义属性的信息?

2 个答案:

答案 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)