2008-11-03 58 views
6

我實例化一個A類(這我從別人 其他進口,所以我不能修改)到類X.的Python:有前置和後置方法

包裝方法調用是否有辦法我可以攔截或打包對A中方法的調用? 即,在下面的代碼,我可以叫

x.a.p1() 

,並得到輸出

X.pre 
A.p1 
X.post 

許多TIA!

class A: 
    # in my real application, this is an imported class 
    # that I cannot modify 
    def p1(self): print 'A.p1' 

class X: 
    def __init__(self): 
     self.a=A() 
    def pre(self): print 'X.pre' 
    def post(self): print 'X.post' 

x=X() 
x.a.p1() 

回答

5

這是我和我的同事們想出了一個解決方案:

from types import MethodType 

class PrePostCaller: 
    def __init__(self, other): 
     self.other = other 

    def pre(self): print 'pre' 
    def post(self): print 'post' 

    def __getattr__(self, name): 
     if hasattr(self.other, name): 
      func = getattr(self.other, name) 
      return lambda *args, **kwargs: self._wrap(func, args, kwargs) 
     raise AttributeError(name) 

    def _wrap(self, func, args, kwargs): 
     self.pre() 
     if type(func) == MethodType: 
      result = func(*args, **kwargs) 
     else: 
      result = func(self.other, *args, **kwargs) 
     self.post() 
     return result 

#Examples of use 
class Foo: 
    def stuff(self): 
     print 'stuff' 

a = PrePostCaller(Foo()) 
a.stuff() 

a = PrePostCaller([1,2,3]) 
print a.count() 

給出:

pre 
stuff 
post 
pre 
post 
0 

所以創建對象的實例時,與PrePostCaller對象把它包。之後,您繼續使用對象,就好像它是包裝對象的實例一樣。有了這個解決方案,你可以在每個實例的基礎上進行包裝。

1

無口哨,或編鐘的解決辦法是寫A類,不只是一個包裝類。

+0

是真的,但我想避免因爲我真正的程序包含比我想包裝更多的類實例。 – 2008-11-03 08:41:19

+0

那麼,調用包裝相同,但把它放在你自己的命名空間。它將有效地作爲一個插入式替代品,並且你不需要改變你的代碼。 – Tomalak 2008-11-03 08:51:06

1

你可以只修改在A實例,並用一個包裝函數替換P1功能:

def wrapped(pre, post, f): 
    def wrapper(*args, **kwargs): 
     pre() 
     retval = f(*args, **kwargs) 
     post() 
     return retval 
    return wrapper 

class Y: 
    def __init__(self): 
     self.a=A() 
     self.a.p1 = wrapped(self.pre, self.post, self.a.p1) 

    def pre(self): print 'X.pre' 
    def post(self): print 'X.post' 
1

我最近剛剛看了一下Python中的裝飾,我不理解他們還沒有,但它似乎我可以解決你的問題。看到布魯斯·埃克爾介紹到裝飾在: http://www.artima.com/weblogs/viewpost.jsp?thread=240808

他對這一主題有幾個帖子。

編輯:三天後我絆倒這篇文章,它展示瞭如何做沒有裝飾類似的任務,什麼是它的問題後,然後介紹了裝飾,並制定了相當完整的解決方案: http://wordaligned.org/articles/echo

0

這裏是我從comp.lang.python的Steven D'Aprano收到。

# Define two decorator factories. 
def precall(pre): 
    def decorator(f): 
     def newf(*args, **kwargs): 
      pre() 
      return f(*args, **kwargs) 
     return newf 
    return decorator 

def postcall(post): 
    def decorator(f): 
     def newf(*args, **kwargs): 
      x = f(*args, **kwargs) 
      post() 
      return x 
     return newf 
    return decorator 

現在你可以猴子補丁類A,如果你想。在生產代碼中這可能不是一個好主意,因爲它會影響到所有類別。 [這是確定用於我的應用程序,因爲它基本上是一個協議轉換器和有完全被處理的每個類的一個實例。]

class A: 
    # in my real application, this is an imported class 
    # that I cannot modify 
    def p1(self): print 'A.p1' 

class X: 
    def __init__(self): 
     self.a=A() 
     A.p1 = precall(self.pre)(postcall(self.post)(A.p1)) 
    def pre(self): print 'X.pre' 
    def post(self): print 'X.post' 


x=X() 
x.a.p1() 

給出所需的結果。

X.pre 
A.p1 
X.post 
1

正如其他人所說,包裝器/裝飾器解決方案可能是最簡單的。出於與您指出的相同的原因,我不建議修改包裝類本身。

如果您有許多外部類,您可以編寫代碼生成器來爲您生成包裝類。既然你在Python中這樣做,你甚至可以實現生成器作爲程序的一部分,在啓動時生成包裝器或其他東西。