2009-11-04 90 views
3

下面是我想要做的事情的要點。我有對象的列表,而且我知道他們有看起來像一個實例方法:在Python中裝飾實例方法

def render(self, name, value, attrs) 
    # Renders a widget... 

我想(essentialy)在運行時裝飾這些功能,因爲我遍歷對象的列表。所以,他們的渲染功能成爲這個:

def render(self, name, value, attrs) 
    self.attrs=attrs 
    # Renders a widget... 

兩個注意事項:

  1. 渲染功能的Django的一部分。我不能在他們的庫中放置一個裝飾器(當然,我可以,但是我必須維護並遷移這個更改)。
  2. 這是一個實例方法。

這裏的一個例子: http://wiki.python.org/moin/PythonDecoratorLibrary

顯示瞭如何將新的實例方法添加到一個類。這裏的區別是我想記住attrs參數後想要回到原來的方法。

回答

7
def decorate_method(f): 
    def wrapper(self, name, value, attrs): 
    self.attrs = attrs 
    return f(self, name, value, attrs) 
    return wrapper 

def decorate_class(c): 
    for n in dir(c): 
    f = getattr(c, n) 
    if hasattr(f, 'im_func'): 
     setattr(c, n, decorate_method(f.im_func)) 

你可能需要一些其他測試,以跳過方法具有不同的簽名,但是,除此之外,decorate_class(whatever)應該做你想做的任何給定whatever類。

+1

你從頭頂寫了這段代碼,不是嗎? :) – Stephan202

+0

@ Stephan202,是的,有沒有你必須編輯的錯字? –

+0

@Alex:是的,請參閱修訂歷史記錄。 'setattr'作爲第二個參數傳遞了'f'而不是'n'。 – Stephan202

3

「經典」方式是子類。這樣你就不必與其他人的課程混淆了。

class someclass(object): 
    def render(self, name, value, attrs): 
     print hasattr(self, 'attrs') 

class my_render(object): 
    def render(self, name, value, attrs): 
     self.attrs = attrs # kind of decorating the function here 
     return super(my_render, self).render(name, value, attrs) 

class my_class(my_render, someclass): 
    pass  

someclass().render(1,2,3) # -> False 
my_class().render(1,2,3) # -> True 

MI的原因是所有類都可以從my_render繼承。我喜歡mixin的概念;-)

class my_otherclass(my_render, someotherclass): pass 
class my_thirdclass(my_render, thirdclass): pass 

# or less explicit 
classlist = [ someclass, someotherclass ] 
newclasses = [ type('my_'+cls.__name__, (my_render,cls), {}) for cls in classlist ] 
+1

有趣的是,你選擇使用MI作爲一個簡單的例子,而不是讓my_render從某個類繼承。 – jamessan

+0

是的,這通常是我怎麼做的,但我希望能夠充分利用裝飾,所以我不必將Django提供的所有各種類型的字段和MI /裝飾在飛行中不管它是否爲InputField,DateTimeField等。 Django在呈現字段時會執行大量的黑魔法。我有一個冒險計算出每個字段在呈現之前都被包裹在'BoundField'中的事實... – Koobz