2010-05-28 72 views
3

的数量,我想装饰一个功能,采用这样的模式:装饰功能,并添加功能保留的说法

def deco(func): 
    def wrap(*a,**kw): 
     print "do something" 
     return func(*a,**kw) 
    return wrap 

的问题是,如果装饰功能已具雏形这样:

def function(a,b,c): return 

装饰时,将原型由可变参数破坏,例如,调用函数(1,2,3,4)将不会导致异常。这是避免这种情况的方法吗? 如何用与装饰(func)相同的原型来定义包装函数?

有什么概念错误?

编辑

我的乖张想法是,以减轻“父方法的调用”,而无需修改签名。喜欢的东西

def __init__(self, something) 
    super(ClassName, self).__init__(something) 

到:

@extended 
def __init__(self, something): 
    ... 

我搞清楚,如果这是可能的,如果这是有道理的。

编辑 亚历克斯指出,下面的代码不给一个例外:

function(1,2,3,4) 

回答

3

decorator module可帮助您创建一个可保留函数签名的装饰器。

因此,您将在调用该函数时得到您期望的异常,并且inspect.getargspec会为您提供正确的签名。

它通过动态构建函数定义并使用exec来工作。不幸的是,这样做并不容易。

0

通过调用result=func(*a,**kw)第一,您在打印“做一些事情”之前拿到类型错误。

def deco(func): 
    def wrap(*a,**kw): 
     result=func(*a,**kw) 
     print "do something" 
     return result 
    return wrap 

@deco 
def function(a,b,c): return 

function(1,2,3) 
# do something 
function(1,2,3,4) 
# TypeError: function() takes exactly 3 arguments (4 given) 
4

当你声明“调用函数(1,2,3,4)不会导致异常”时,你错了。检查出来:

>>> def deco(f): 
... def w(*a, **k): 
...  print 'do something' 
...  return f(*a, **k) 
... return w 
... 
>>> def f(a, b, c): return 
... 
>>> f(1, 2, 3, 4) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in ? 
TypeError: f() takes exactly 3 arguments (4 given) 
>>> decorated = deco(f) 
>>> decorated(1, 2, 3, 4) 
do something 
Traceback (most recent call last): 
    File "<stdin>", line 1, in ? 
    File "<stdin>", line 4, in w 
TypeError: f() takes exactly 3 arguments (4 given) 

正如你看到的,你得到完全相同的例外,因为当调用同样未修饰的f(虽然在您添加后print)。

为了保留包装函数的元数据(名称,文档字符串,使用functools.wraps。预测包装函数在调用时会引发(以避免在调用它之前做其他工作)总是很难(通常“不可能”),因为它是等价的到停止问题;当你只关心提出一个参数名称和数字不匹配的类型错误,并想要将特殊的异常情况与其他异常情况区别对待时,“特别困难” - 一个特殊的要求; -0 )。

如果你坚持认为你绝对需要它(或许可以解释为什么?)我会很高兴(当然,不是高兴,但会咬紧我的牙和做,毕竟我在我面前有一个长周末;-)带你走迷宫般的小路。

3

这是一个技巧,涉及从装饰函数获取原始参数说明,然后通过使用相同参数评估字符串来创建lambda表达式。装饰者然后被包裹在这个lambda中,因此到外界它具有相同的参数名称和默认值:

import inspect, time 
import functools 

def decorator_wrapper(old_function, new_function): 
    args, arglist, kw, default = inspect.getargspec(old_function) 
    args = list(args) 

    if arglist: 
     args.append(arglist) 

    if kw: 
     args.append(kw) 

    callstring = inspect.formatargspec(args, arglist, kw, default, formatvalue=lambda value: "") 
    argstring = inspect.formatargspec(args, arglist, kw, default)[1:-1] 

    unique_name = "_func" + str(int(time.time())) 
    codestring = "lambda " + argstring + " : " + unique_name + callstring 
    decorated_function = eval(codestring, {unique_name: new_function}) 

    return functools.wraps(old_function)(decorated_function) 
+0

聪明但不安全:( – 2rs2ts 2013-07-31 23:53:00