2017-05-31 31 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__'一樣。 –