2017-05-31 22 views
1

因此,假设我想写我自己的类,重写__getattribute__函数。每当有人调用未定义的属性时,我都希望它生成一个随机int。获取关于没有__dict__或dir()的定义属性的信息

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

你的递归是由所有属性必须生活在'__dict__'假设引起的。你应该首先检查* class *。 –

回答

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__,而不是一个更清洁和更简单的选项。

最后但并非最不重要的是,您应该使用super().__getattribute__(...)来代替使用object.__getattribute__(self, ...),以确保您不会在类层次结构中跳过任何其他__getattribute__挂钩。

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) 
+0

@MartijnPieters:哎呀。固定。 – user2357112

+1

并在实例中测试attr。__dict__',然而用于检索该对象的方法是不够的;在'__getattribute__'实例上可以找到其他属性,但是这种方式会错过。像'instance .__ class__'一样。 –