2009-06-20 70 views
6

我習慣於Python允許一些巧妙的技巧將功能委託給其他對象。一個例子是委託包含的對象。在Python中模擬成員測試:正確委託__contains__包含對象

但它接縫,我沒有運氣,當我想委託__contains __:

class A(object): 
    def __init__(self): 
     self.mydict = {} 
     self.__contains__ = self.mydict.__contains__ 

a = A() 
1 in a 

我得到:

Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
TypeError: argument of type 'A' is not iterable 

我所作出錯誤的?當我調用.__包含__(1)時,一切都很順利。我甚至試圖在A中定義一個__iter __方法來使A看起來更像一個迭代,但它沒有幫助。我在這裏錯過了什麼? (在Python 2除了在傳統的班,你應該反正用)在類中定義的時候,而不是在實例

回答

18

特殊的方法,如__contains__只有特殊。

所以,做你的代表團在類級別:

class A(object): 
    def __init__(self): 
     self.mydict = {} 

    def __contains__(self, other): 
     return self.mydict.__contains__(other) 

其實我更喜歡拼寫後者作爲return other in self.mydict,但是這是一個輕微的風格問題。如果當「完全動態的每個實例重定向特殊方法」(如提供的舊式類)不可或缺時,用新式類實現它並不難:你只需要每個實例這種特殊需要被包裝在自己的特殊類別中。例如:

class BlackMagic(object): 
    def __init__(self): 
     self.mydict = {} 
     self.__class__ = type(self.__class__.__name__, (self.__class__,), {}) 
     self.__class__.__contains__ = self.mydict.__contains__ 

從本質上講,之後的黑魔法重新分配self.__class__到一個新的類對象的點點(其行爲就像前面的一個,但有一個空的字典和除這一個self沒有其他情況下),任何地點在舊式課程中,您將分配給self.__magicname__,而不是分配給self.__class__.__magicname__(並確保它是內置的或staticmethod,不是普通的Python函數,除非在某些不同的情況下,您確實希望它在接收self時收到self調用實例)。

順便說一句,in運營商在這個BlackMagic類的一個實例是更快,因爲它發生,比任何先前提出的解決方案 - 或者至少使我和我平時的信賴-mtimeit(去測量直接到built-in method,而不是遵循正常的查找路由涉及繼承和描述符,削減了一點開銷)。

自動執行self.__class__ -per實例想法並不難寫一個元(它可以做在生成的類的__new__方法骯髒的工作,也許還設置所有魔法的名字實際上對類分配,如果分配在實例上,通過__setattr__或許多,許多屬性)。但只有當這個特性的需求非常普遍時(例如,移植一個巨大的古代Python 1.5.2項目,它可以自由地將「每實例特殊方法」用於現代Python,包括Python 3),這纔是合理的。

我是否推薦「聰明」還是「黑魔法」解決方案?不,我不這樣做:幾乎總是以簡單,直接的方式來做事情會更好。但「差不多」在這裏是一個重要的詞,很高興有這樣的先進的「鉤子」爲罕見的,但並非不存在的情況下,他們的使用可能實際上是有保證的。

+0

好的,這對我做了一些解釋,但對我來說並不完全令人滿意,因爲這種類型的授權需要額外的時間(我可以在舊式類中保存 - 爲什麼我不應該使用它們?)。 – Juergen 2009-06-20 20:49:45