2014-03-24 61 views
9

問題涉及到哪一個更好用用於哪個用例,而不是關於技術背景。用例屬性與描述符vs. __getattribute__

在Python中,你可以控制通過財產,一個描述,或魔術方法屬性的訪問。在哪個用例中哪一個是最pythonic?它們都似乎具有相同的效果(請參閱下面的示例)。

我正在尋找像一個答案:

  • 物業:應在以下情況下可使用...
  • 描述:在... ...的情況下,它應該被用來代替屬性。
  • 不可思議的方法:只有在...時使用。

用例將是可能無法在__init__方法進行設置,例如,因爲該對象不存在於數據庫又一個屬性,但在稍後的時間。每次訪問屬性時,都應嘗試設置並返回。

作爲一個適用於複製&粘貼到Python shell的示例,有一個類只在第二次請求它時才顯示其屬性。那麼,哪一個纔是最好的方式,還是有不同的情況,其中一個更可取?這裏有三種方式實現:

隨着物業 ::

class ContactBook(object): 
    intents = 0 

    def __init__(self): 
     self.__first_person = None 

    def get_first_person(self): 
     ContactBook.intents += 1 
     if self.__first_person is None: 
      if ContactBook.intents > 1: 
       value = 'Mr. First' 
       self.__first_person = value 
      else: 
       return None 
     return self.__first_person 

    def set_first_person(self, value): 
     self.__first_person = value 

    first_person = property(get_first_person, set_first_person) 

隨着__getattribute__ ::

class ContactBook(object): 
    intents = 0 

    def __init__(self): 
     self.first_person = None 

    def __getattribute__(self, name): 
     if name == 'first_person' \ 
       and object.__getattribute__(self, name) is None: 
      ContactBook.intents += 1 
      if ContactBook.intents > 1: 
       value = 'Mr. First' 
       self.first_person = value 
      else: 
       value = None 
     else: 
      value = object.__getattribute__(self, name) 
     return value 

描述 ::

class FirstPerson(object): 
    def __init__(self, value=None): 
     self.value = None 

    def __get__(self, instance, owner): 
     if self.value is None: 
      ContactBook.intents += 1 
      if ContactBook.intents > 1: 
       self.value = 'Mr. First' 
      else: 
       return None 
     return self.value 


class ContactBook(object): 
    intents = 0 
    first_person = FirstPerson() 

EAC H的一個它有這種行爲::

book = ContactBook() 
print(book.first_person) 
# >>None 
print(book.first_person) 
# >>Mr. First 
+1

'__getattribute__'是**總是被調用,'__getattr__'只會在沒有通過其他方式找到屬性時被調用。 '__getattribute__'是'property'(和其他描述符對象)完成工作的機制。 –

+0

這是一個_descriptor_不是_decorator_ – Eric

+0

@Eric:謝謝,我編輯了這個問題。 – Iodnas

回答

11

基本上,使用最簡單的一個,你可以。粗略地說,複雜性/重型性的順序是:常規屬性property__getattr__,__getattribute__ /描述符。 (__getattribute__和自定義的描述,你可能不會需要很經常做兩件事。)這導致拇指的一些簡單的規則:

  • 不要使用property如果一個正常的屬性將正常工作。
  • 如果property有效,請不要編寫自己的描述符。
  • 如果property有效,請勿使用__getattr__
  • 如果__getattr__可以使用,請勿使用__getattribute__

更具體地說:使用屬性來自定義一個或一小組屬性的處理;使用__getattr__來自定義所有屬性的處理,或者除了小集以外的所有屬性;如果您希望使用__getattr__,則使用__getattribute__但它不起作用;編寫你自己的描述符類,如果你正在做一些非常複雜的事情。

當您有一個或一組屬性的要獲取/設置要掛鉤的屬性時,您可以使用property。也就是說,你需要像obj.propobj.prop = 2這樣的東西祕密地調用你寫的函數來定製發生的事情。

你會使用__getattr__當你想要做的,你實際上並不希望單獨定義所有的這麼多的屬性,而是要自定義的全屬性的訪問過程作爲一個整體。換句話說,不是掛在obj.prop1obj.prop2等等,你有很多,你想能夠鉤進obj.<anything>,並處理一般。

但是,__getattr__仍然不會讓您重寫真正存在的屬性會發生的情況,它只是讓您使用全局處理來使用任何可能會引發AttributeError的屬性。使用__getattribute__可讓您連接處理的所有內容,甚至可以正常運行而不會與__getattribute__混淆的屬性。因此,使用__getattribute__有可能打破相當基本的行爲,所以如果您考慮使用__getattr__並且它不夠用,則應該只使用它。它也可以有顯着的性能影響。例如,如果要包裝定義某些屬性的類,並且希望能夠以自定義方式包裝這些屬性,以便在某些情況下照常工作,但在其他情況下獲得自定義行爲,則可能需要使用__getattribute__的情況。

最後,我會說寫你自己的描述符是一個相當先進的任務。 property是一個描述符,對於大概95%的情況,這是唯一需要的。一個很好的例子說明你爲什麼寫自己的描述符的原因是here:基本上,如果你不得不寫幾個具有相似行爲的property,描述符可以讓您將常見行爲分解以避免代碼重複。例如,使用自定義描述符來驅動像Django和SQLAlchemy這樣的系統。如果你發現自己寫的東西在複雜程度上,你可能需要寫一個自定義描述符。

在你的例子中,property將是最好的選擇。如果您在__getattribute__內部做if name == 'somespecificname',通常(並非總是)紅旗。如果你只需要特別處理一個特定的名字,你可以做到這一點,而不會屈服於__getattribute__的水平。同樣,如果您爲__get__編寫的所有內容都是您可以使用屬性的getter方法編寫的東西,那麼編寫自己的描述符就沒有意義。

+1

在使用'__getattribute__'覆蓋之前,我可以使用描述符對象。我**很少**需要使用'__getattribute__'鉤子。 :-) –

+0

也許,是的。我認爲'__getattribute__'和描述符都非常罕見,很難說哪一個在複雜性上最先出現。 – BrenBarn

+1

我經常使用描述符;當將一個類定義的對象作爲屬性訪問時綁定到該實例是一個很好的功能。 –

2

__getattribute__是使property(和其他描述符)鉤在首位的工作,並呼籲對象所有的屬性訪問。當一個屬性或一個自定義描述符不足以滿足您的需求時,請將其視爲較低級別的API。 __getattr____getattribute__調用時沒有通過其他方式找到屬性,作爲後備。

對於固定名稱的動態屬性使用property,對於更具動態性質的屬性(例如以算法方式映射到值的一系列屬性)使用__getattr__

當需要時使用描述符任意對象綁定到實例。當你需要用更高級的東西替換方法對象時,最近的一個例子是class-based decorator wrapping methods,它需要支持方法對象的附加屬性和方法。一般來說,當你仍然在用標量屬性來思考時,你不需要描述符。