如何理解python decorator参数传递

时间:2014-06-04 14:17:17

标签: python decorator

我尝试了解python decorator

def dec(func):
    def wrap(*args,**kw):
        print args, kw
        return func(*args,**kw)
    return wrap

@dec
def myfunc(a=1,b=2,c=3):
    return a+b+c


>>> myfunc()
() {}
6
>>> myfunc(1,2,3)
(1, 2, 3) {}
6
>>> myfunc(1,2,c=5)
(1, 2) {'c': 5}
8
>>> 

当我运行myfunc()时,args和kw什么都没有,但当我运行myfunc(1,2,3)myfunc(1,2,c=5)时,args和kw被传递给dec函数。

据我所知,

@dec
def myfunc(...)

等于myfunc = dec(myfunc)< - 此处未提及任何参数。

如何将参数传递给wrap中的dec函数?怎么理解这些?

3 个答案:

答案 0 :(得分:3)

不确定我是否理解你的问题,但myfunc参数的默认值仅为myfunc所知 - 你的装饰者不知道它们,因此无法打印它们。

这就是原因:

myfunc()

导致打印:

() {}

装饰器的*args**kw都是空的,但在这种情况下,装饰的函数将使用默认值。

在第二种和第三种情况下,您将打印参数,因为它们被显式传递给修饰函数:

 def wrap(*args,**kw): <- this is what is actually called when you invoke decorated function, no default values here
    print args, kw
    return func(*args,**kw) <- this is the function you decorate
    #if func has default parameter values, then those will be used 
    #when you don't pass them to the wrapper but only **inside** func
 return wrap

编辑: 看起来你错误地调用了装饰函数来修饰函数:

myfunc = dec(myfunc) 

使用myfunc修饰dec,相当于

@dec
def myfunc(...)

另一方面,在使用其中任何一个之后:

myfunc(whatever)

调用装饰器中定义的wrap函数,该函数将依次调用原始myfunc

答案 1 :(得分:1)

装饰器是函数包装器。它们返回一个函数,将原始函数包装成一些预处理和后处理代码,但仍然需要调用原始函数(通常使用与没有装饰器时调用它相同的参数)。

Python有两种类型的参数,位置和关键字参数(这与装饰器无关,这是通用的python基础知识)。 *用于位置(内部是列表),**用于关键字(字典)参数。通过指定两者,您允许装饰器接受所有可能类型的参数,并将它们传递给底层函数。但是,呼叫合同仍由您的功能定义。例如。如果它只接受关键字参数,那么当装饰器函数通过位置参数时它将失败。

在您的特定示例中,您有一些预处理代码(即在调用原始函数之前运行的代码)。例如,在此代码中,您可以打印出原始函数可能无法一起接受的参数*args,因为它不接受任何位置参数。

您不一定必须通过*args**kwargs。实际上,您可以定义一个装饰器,根据您传递的参数做出一些决定,这些参数将作为参数提供给原始函数,例如。

def dec(fun):
    def wrap(*args, **kwargs):
        if 'a' in kwargs:
            return fun(kwargs[a])
        else:
            return fun(*args)
    return wrap

答案 2 :(得分:1)

另一种思考方式是:

def wrapper(some_function):
    def _inner(*args, **kwargs):
        #do some work
        return func(*args, **kwargs)
    return _inner

@wrapper
def foo(a, b, c):
    return "Foo called, and I saw %d, %d, %d" % (a, b, c)

...你得到的结果大致类似于以下内容:

def foo(a, b, c):
    #do some work
    return "Foo called, and I saw %d, %d, %d" % (a, b, c)

这不完全正确,因为#do some work在实际foo()调用之前正在发生,但作为近似值,这就是您所获得的。因此,如果存在,则包装器不能真正“看到”foo()的默认参数。因此,更好的方法是考虑它:

#always execute this code before calling...
def foo(a, b, c):
    return "Foo called and I saw %d, %d, %d" % (a, b, c)

所以真正基本的东西可能看起来像这样。

def wrap(f):
...     def _inner(*a, **k):
...             new_a = (ar*2 for ar in a)
...             new_k = {}
...             for k, v in k.iteritems():
...                     new_k[k] = v*2
...             return f(*new_a, **new_k)
...     return _inner
... 
>>> def foo(a=2, b=4, c=6):
...     return "%d, %d, %d" % (a, b, c)
... 
>>> foo()
'2, 4, 6'
>>> foo(1, 5, 7)
'1, 5, 7'
>>> foo = wrap(foo)    #actually wrapping it here
>>> foo()
'2, 4, 6'
>>> foo(3, 5, 6)
'6, 10, 12'
>>> foo(3, 5, c=7)
'6, 10, 14'
>>>