2009-06-29 128 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))