2015-10-21 55 views
1

我的問題是看起來象下面這樣:如何使用裝飾器將參數綁定到靜態方法函數?

class foo(obj): 

    def __init__(self, st='123'): 
     self.st = st 

    def process(self, x): 
     self.st += x 

    @staticmethod 
    def do_foo(x, myfoo=None): 
     if myfoo is None: 
      myfoo = foo() 
     myfoo.process(x) 

def wrapper(fn, st): 

    foo_func = foo(st) 
    foo.do_foo = functools.partial(foo.do_foo, myfoo=foo_func) 
    fn() 
    print foo_func.st 

    return wrap 

@wrapper('stst') 
def pro(): 
    foo.do_foo('double') 

def pro2(): 
    foo.do_foo('double') 

pro2() # <--- normal foo.do_foo 
pro() # <--- partialed foo.do_foo 
pro2() # <--- partialed foo.do_foo 

我想創建wrapper裝飾爲包裝的靜態方法foo.do_foo與定製foo類和pro()執行後,這個裝飾能夠跨越Foo對象做一些工作。即保存變量值。

在上面的代碼中,每個包裝器如何在全局範圍內永久更改foo.do_foo,而不僅僅是在修飾器範圍中對其進行更改。

那麼如何讓foo.do_foo只在修飾器範圍內更改而不是全局?

回答

1

下面是基於您的評論鏈接到gist.github.com code一個稍微不同的答案(這似乎是基於我的回答的第一個版本)。

就像我一開始說,這聽起來像你對我所要做的就是讓wrapper()一個裝飾廠功能,而不是一個裝飾本身是什麼 - 換句話說使其成爲基於創建並返回一個裝飾功能的參數(一個或多個)。

正如我在回覆您的評論時所提到的,問題是staticmethod本質上是一個全局變量,如果包裝函數改變它,這將影響所有後續調用 - 這是問題的根源。

解決方法是在裝飾函數調用裝飾函數之前,裝飾器創建Prof.do函數,然後在之後進行恢復,以便更改僅影響對通過它進行的函數調用的wrapped()函數。這可以防止Prof.do在其他可能創建的包裝函數中搞亂其他調用。它也可以防止其效果的積累。

我已經封裝了更改並通過將其置於contextmanager輔助函數中來恢復靜態方法。需要這樣做的一個缺點是增加了打包函數調用的開銷。

import contextlib 
import functools 

class Prof(object): 
    def __init__(self, p='s'): 
     self.p = p 

    @staticmethod 
    def do(x, obj=None): 
     if obj is None: 
      obj = Prof() 
     obj.dprint(x) 
     print 

    def dprint(self, x): 
     print self.p, x 
     self.p += x 

def wrapper(st): 
    @contextlib.contextmanager 
    def prof_context(obj): # could also be defined outside of wrapper function 
     # save current staticmethod and replace it with partial below 
     saved_method, Prof.do = Prof.do, functools.partial(Prof.do, obj=obj) 
     yield 
     # undo staticmethod modification 
     Prof.do = staticmethod(saved_method) 

    def decorator(fn): 
     @functools.wraps(fn) 
     def wrapped(): 
      obj = Prof(st) 
      print 'current: obj.p is %r' % obj.p 
      with prof_context(obj): 
       fn() 

     return wrapped 

    return decorator 

def do_p(): 
    Prof.do('do') 

@wrapper('do_p2') 
def do_p2(): 
    Prof.do('do2') 

print '#A do_p():' 
do_p() 
print '#B do_p2():' 
do_p2() 
print '#C do_p():' 
do_p() 
print '#D do_p2():' 
do_p2() 
print '#E do_p():' 
do_p() 
print '#F do_p():' 
do_p() 

輸出:

#A do_p(): 
s do 

#B do_p2(): 
current: obj.p is 'do_p2' 
do_p2 do2 

#C do_p(): 
s do 

#D do_p2(): 
current: obj.p is 'do_p2' 
do_p2 do2 

#E do_p(): 
s do 

#F do_p(): 
s do 
+0

PLZ看到這個代碼https://gist.github.com/tywtyw2002/c885bd917430e0563288,爲什麼'C'打印'do_p2do2 do'代替單曲do'。我想要這樣的代碼,如果有裝飾器在裝飾器中更改字符串參數,沒有裝飾器字符串應該提醒默認值。 – tywtyw2002

+0

問題是類和它的靜態方法是有效的全局變量,當你用'functools.partial()'改變後者時,最後賦值被所有後續調用使用。如果我想出解決方案,我會更新我的答案。 – martineau

+0

查看最新的更新,我認爲可能會完成你想要做的事情。 – martineau

相關問題