我尝试了解python装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def 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函数。
我所知,
1
2@dec
def myfunc(...)
等于myfunc = dec(myfunc)
如何将参数传递给dec中的wrap函数? 如何理解这些?
不知道我是否正确理解了您的问题,但是myfunc参数的默认值仅是myfunc知道的-您的装饰器不了解它们,因此无法打印它们。
这就是为什么:
1myfunc()
导致打印:
1() {}
装饰器的*args和**kw均为空,但是在这种情况下,装饰的函数将使用默认值。
在第二种和第三种情况下,您将打印出参数,因为它们已显式传递给装饰函数:
1
2
3
4
5
6def 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
编辑:
看起来您会误以为装饰函数调用装饰函数:
1myfunc = dec(myfunc)
使用dec装饰myfunc,等效于
1
2@dec
def myfunc(...)
另一方面,在使用其中任何一个之后:
1myfunc(whatever)
调用装饰器中定义的wrap函数,该函数将依次调用原始的myfunc
但是如何将它们传递给装饰函数?
您在def wrap(*args,**kw): print args, kw return func(*args,**kw) 中显式传递了它们
谢谢,但是myfunc = dec(myfunc)此处没有提及任何参数。
请注意,您无需在此处调用myfunc,只需使用dec包装(装饰)它即可。
要调用它,您仍然需要使用myfunc(...),并且您将获得与使用@dec时相同的结果。
谢谢你的解释。 恐怕我花了太多时间。 再次感谢。
别担心,这就是SO的目的;)
另一种思考的方式是说:
1
2
3
4
5
6
7
8
9def 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)
...您得到的结果大致类似于以下内容:
1
2
3def foo(a, b, c):
#do some work
return"Foo called, and I saw %d, %d, %d" % (a, b, c)
这并不完全正确,因为#do some work发生在实际的foo()调用之前,但是作为近似,这就是您所得到的。因此,包装程序实际上无法"看到" foo()的默认参数(如果存在)。因此,考虑它的更好方法可能是:
1
2
3#always execute this code before calling...
def foo(a, b, c):
return"Foo called and I saw %d, %d, %d" % (a, b, c)
因此,一些基本的东西可能看起来像这样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24def 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'
>>>
装饰器是函数包装器。他们提供了一个将原始代码包装到一些预处理和后期处理代码中的函数,但是仍然需要调用原始函数(通常使用与没有装饰器时调用的参数相同的参数)。
Python有两种类型的参数,即位置参数和关键字参数(这与装饰器无关,这是通用的python基础知识)。 *用于位置(内部是列表),**用于关键字(字典)自变量。通过指定两者,您可以让装饰器接受所有可能的参数类型,并将其传递给基础函数。但是,调用合同仍由您的函数定义。例如。如果仅接受关键字参数,则装饰器函数通过位置参数传递时将失败。
在您的特定示例中,您具有一些预处理代码(即将在调用原始函数之前运行的代码)。例如,在此代码中,您可以打印出参数*args,因为您的原始函数可能不接受所有参数,因为它没有任何位置参数。
您不必必须通过*args和**kwargs。实际上,您可以定义一个装饰器,该装饰器根据传入的参数来决定要提供给原始函数的参数,例如
1
2
3
4
5
6
7def dec(fun):
def wrap(*args, **kwargs):
if 'a' in kwargs:
return fun(kwargs[a])
else:
return fun(*args)
return wrap