我尝试了解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
函数?怎么理解这些?
答案 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'
>>>