2009-06-29 251 views
5

我很高興看到decorator python模塊(3.0)的最新版本。它看起來比以前的迭代更清晰(例如,語法比以往更加含蓄)。Python裝飾3.0和裝飾的參數

然而,它似乎對裝飾者的糟糕的支持(例如「酸」的語法,以可怕的方式拉伸隱喻),這些裝飾者自己接受論證。有沒有人有一個很好的例子,你如何幹淨地使用decorator 3.0?

def substitute_args(fun, arg_sub_dict): 
     def wrapper(arg): 
     new_arg = arg_sub_dict.get(arg, arg) 
     return fun(new_arg) 

     # some magic happens here to make sure that type signature, 
     # __name__, __doc__, etc. of wrapper matches fun 

return wrapper 
+0

裝飾家:http://pypi.python.org/pypi/decorator/ – 2009-06-29 20:07:27

回答

7

在這種情況下,您需要讓您的函數返回裝飾器。 (什麼都可以通過間接的另一個層面來解決......)

from decorator import decorator 
def substitute_args(arg_sub_dict): 
    @decorator 
    def wrapper(fun, arg): 
    new_arg = arg_sub_dict.get(arg, arg) 
    return fun(new_arg) 
    return wrapper 

這意味着substitute_args不是一個裝飾本身,它是一個裝飾工廠。沒有decorator模塊的等效模塊。

def substitute_args(arg_sub_dict): 
    def my_decorator(fun): 
    def wrapper(arg): 
     new_arg = arg_sub_dict.get(arg, arg) 
     return fun(new_arg) 
    # magic to update __name__, etc. 
    return wrapper 
    return my_decorator 

三個層次的深度不是很方便,但要記住他們兩個是當函數定義:

@substitute_args({}) # this function is called and return value is the decorator 
def f(x): 
    return x 
# that (anonymous) decorator is applied to f 

即相當於:

def f(x): 
    return x 
f = substitude_args({})(f) # notice the double call 
+0

這是非常有用的。謝謝! – YGA 2009-07-02 02:26:19

-2

這裏是另一個我剛剛發現的方式:檢查你的裝飾器的第一個(也是唯一的)參數是否可調用;如果是這樣,你就完成了,並可以返回你的行爲修改包裝方法(本身用functools.wraps裝飾來保留名稱和文檔字符串)。

在另一種情況下,應該存在一個或多個命名或位置參數;你可以收集這些參數並返回一個callable,它接受一個callable作爲第一個參數,並返回一個包裝方法 - 並且由於該描述符合decorator方法的描述,所以返回這個非常簡單的裝飾方法!我在這裏使用functools.partial來獲得我的裝飾者版本,is_global_method(我現在正在進行工作 - 它的實現當然是無稽之談,如下所示,這只是爲了演示裝飾作品)。

此解決方案似乎可行,但肯定需要更多測試。如果你使我們的眼睛五射,你可以看到訣竅只有三或四行作爲一種模式來記住。現在我想知道我是否可以將這種功能包裝到另一個裝飾器中?啊,它的元素!

from functools import wraps 
from functools import partial 

_    = print 
is_instance_of = isinstance 
is_callable  = lambda x: hasattr(x, '__call__') 

def is_global_method(x, *, name = None): 
    if is_callable(x): 
    @wraps(x) 
    def wrapper(*P, **Q): 
     return { 'name': name, 'result': x(*P, **Q), } 
    return wrapper 
    # assert is_instance_of(x, str) # could do some sanity checks here 
    return partial(is_global_method, name = x) 

@is_global_method 
def f(x): 
    """This is method f.""" 
    return x ** 2 

@is_global_method('foobar') 
def g(x): 
    """This is method g.""" 
    return x ** 2 

_(f.__name__) 
_(f.__doc__) 
_(f(42)) 
_(g.__name__) 
_(g.__doc__) 
_(g(42))