2012-02-08 54 views
2

假設我有一個類被用作像這樣的裝飾:Python的裝飾方法可贖回外父類

class specialmethod: 
    def __init__(self,func): 
    self.func = func 

    def __call__(self,arg1,arg2): 
    print arg1,arg2 
    self.func(arg1,arg2) 

但正在裝修的方法是在另一個類中,也必須利用self(中自中值相關的函數體裝修後)

class A: 
    @specialmethod 
    def dosomething(self,data,otherdata): 
    #this guy down here isn't getting the right value for self, an instance of A 
    print type(self) 

Traceback (most recent call last): 
    File "deco.py", line 18, in <module> 
    a.dosomething('foo','bar') 
    File "deco.py", line 7, in __call__ 
    self.func(arg1,arg2) 
TypeError: dosomething() takes exactly 3 arguments (2 given) 

反正是有保留的方式self,如果它不是裝飾,將工作?我試過顯式傳遞它,但它仍然期望錯誤的參數數量。

編輯

我要澄清的是自我在dosomething最終函數體的預期值應參照A實例......不找得到裝飾實例爲裝飾功能。

回答

4

當你使用像裝飾器這樣的類時,你必須認識到,除非你明確地實現它,否則將函數轉化爲方法(描述符協議,特別是__get__方法)的正常魔法將不能用於你的類。你可以手動執行此操作,通過實施__get__方法返回一個新類型,記得它是從,以及檢索的實際調用來調用實例,但它是非常小的收益了很多工作。

更常見的做法是不使用一個實際的類作爲裝飾,而總是有裝飾返回一個實際的Python功能。在這種特殊情況下,有沒有真正在保持類在所有的點,所以你可以只是做:

def specialmethod(f): 
    @functools.wraps(f) 
    def wrapper(arg1, arg2): 
     print arg1, arg2 
     f(arg1, arg2) 
    return wrapper 

...但是,如果你真的想保持類,你可以這樣做的做這樣的事情:

class _specialmethod: 
    def __init__(self,func): 
    self.func = func 

    def __call__(self,arg1,arg2): 
    print arg1,arg2 
    self.func(arg1,arg2) 

def specialmethod(f): 
    wrapper_class = _specialmethod(f) 
    @functools.wraps(f) 
    def wrapper(arg1, arg2): 
     wrapper_class(arg1, arg2) 
    return wrapper 
+0

會第一種方法仍然可以工作,即使被裝飾的功能是一個類中的方法?這兩個參數之前的附加'self'暗示了嗎? – DeaconDesperado 2012-02-08 18:52:46

+0

如果被裝飾的功能是類中的一種方法,則兩種方式都可以工作;在這兩種情況下,'arg1'都是接收實例的參數,'self'在封裝函數中。 (我從你的例子中複製了命名,但通常你在包裝器定義和對'f'的調用中都使用'* args,** kwargs'。在這種情況下'self'是'args'中的第一項元組。) – 2012-02-08 18:56:23

+0

工作很好,謝謝!最後一個問題,如果裝飾的方法是'A'的魔法方法之一,比如'__call__',它的行爲會有所不同? – DeaconDesperado 2012-02-08 18:59:29