python装饰器学习笔记
刚开始学python时,装饰器这段内容学的晕乎乎的,只知道用@
符号可以给函数添加额外功能。记得有次面试被问到装饰器直接就懵逼了。最近完整学了一遍关于装饰器的基础知识,感觉明白了不少。
从'万物皆对象'开始
python中,函数也是对象,也可以作为对象赋值给变量
>>> def hello(name="world"):
return 'hello, ' + name
>>> nihao = hello
>>> print nihao
<function hello at 0x0000000002B20128>
删除掉原来的函数,变量的赋值依然没变:
>>> del hello
>>> print nihao
<function hello at 0x0000000002B20128>
>>> nihao()
'hello, world'
既然函数是对象,那么它也可以作为另一个函数的返回值:
>>> def get_talk():
def hi(name="world"):
return 'hi, ' + name
return hi
>>> print get_talk
<function get_talk at 0x0000000003235128>
执行get_talk
函数, 返回的是hi
函数:
>>> print get_talk()
<function hi at 0x0000000003235748>
继续执行,才执行到了hi
函数:
>>> print get_talk()()
hi, world
既然函数是对象,那么它也可以作为另一个函数的参数:
>>> def doSomethingBefore(func):
print "I do something before then I call the function you gave me"
print func()
>>> doSomethingBefore(hello)
I do something before then I call the function you gave me
hello, world
简单的装饰器
上面的例子中,函数既可以作为另一个函数的参数,也可以作为另一个函数的返回值,把它们综合起来,就可以写出一个简单的装饰器了:
>>> def my_decorator(a_function_to_decorate):
def wrapper():
print "Before the function runs"
a_function_to_decorate()
print "After the function runs"
return wrapper
在这里,my_decorator
接收一个函数a_function_to_decorate
作为参数输入,并将a_function_to_decorate
经过装饰处理后,以另一个函数wrapper
的形式返回。
定义一个待装饰的函数:
def a_function():
print "I am a alone function"
把a_function
作为参数传给装饰器:
>>> func = my_decorator(a_function)
>>> func
<function wrapper at 0x000000000325D668>
这时,变量func
就被赋值为由a_function
经过装饰后的wrapper
函数,执行它,就可以看到装饰器的效果:
>>> func()
Before the function runs
I am a alone function
After the function runs
也可以直接将装饰的结果赋值给原函数:
>>> a_function = my_decorator(a_function)
>>> a_function()
Before the function runs
I am a alone function
After the function runs
这里等效为python的装饰器语法@
:
>>> @my_decorator
def another_function():
print "Leave me alone"
>>> another_function()
Before the function runs
Leave me alone
After the function runs
带输出信息的装饰器
定义这样一个装饰器和待装饰函数:
>>> def my_decorator(func):
print "I am a decorator! I am executed only when you decorate a function."
def wrapper():
print "I am function returned by the decorator"
func()
return wrapper
>>> def lazy_function():
print "zzzzzzzz"
将lazy_function
用装饰器装饰:
>>> decorated_function = my_decorator(lazy_function)
I am a decorator! I am executed only when you decorate a function.
>>> decorated_function
<function wrapper at 0x000000000325D908>
这里decorated_function = my_decorator(lazy_function)
,执行my_decorator
函数,先打印信息,再把由lazy_function
经过装饰生成的函数wrapper
返回给decorated_function
变量。
再执行这个被装饰后的函数就和前面一样了:
>>> decorated_function()
I am function returned by the decorator
zzzzzzzz
依然可以用装饰器语法:
>>> @my_decorator
def lazy_function_1():
print "zzzzzzzz"
I am a decorator! I am executed only when you decorate a function.
>>> lazy_function_1()
I am function returned by the decorator
zzzzzzzz
和前面一样,定义被装饰函数的时候,会打印信息。
带参数的装饰器 Beta 1.0
装饰器是以函数作为输入,以函数作为输出的函数。既然是函数,那当然可以带一些别的参数了:
>>> def my_decorator(func, arg1, arg2):
print "I am a decorator! I get two args:", arg1, arg2
def wrapper():
print "I am function returned by the decorator. I get two args:", arg1, arg2
func()
return wrapper
>>> def lazy_function():
print "zzzzzzzz"
这里的装饰器除了接收一个函数作为参数外,还接收另外两个字符变量,除此之外和前一个例子没什么区别。
>>> decorated_func = my_decorator(lazy_function, 'a', 'b')
I am a decorator! I get two args: a b
>>> decorated_func()
I am function returned by the decorator. I get two args: a b
zzzzzzzz
创建和执行被装饰函数时,会将参数a
, b
打印出来。
运用装饰器语法:
>>> @my_decorator(arg1='a', arg2='b')
def lazy_function_1():
print "zzzzzzzz"
Traceback (most recent call last):
File "<pyshell#61>", line 1, in <module>
@my_decorator(arg1='a', arg2='b')
TypeError: my_decorator() takes exactly 3 arguments (2 given)
出错了。。。。
@my_decorator(arg1='a', arg2='b')
def ...
相当于
decorator = my_decorator(arg1='a', arg2='b')
@decorator
def ...
也相当于
lazy_function_1 = my_decorator(arg1='a', arg2='b')(lazy_function_1)
这里看着就别扭了,有两处错误:
my_decorator
函数参数个数不对;my_decorator
函数执行过后的结果是本应该是被装饰后的函数,这时再接收带装饰函数作为输入,不合逻辑。
所以,以上的带参数装饰器连@
符号都用不了,都不好意思说它是装饰器了。
要想使用@
符号,还要继续封装。
装饰器的生成函数
装饰器也是函数,那它也可以作为另一个函数的输出了。
>>> def decorator_maker():
print "I make decorators! I am executed only once: " \
"when you make me create a decorator."
def my_decorator(func):
print "I am a decorator! I am executed only when you decorate a function."
def wrapped():
print "I am the wrapper around the decorated function. " \
"I am called when you call the decorated function. "
func()
print "As the wrapper, I return the RESULT of the decorated function."
print "As the decorator, I return the wrapped function."
return wrapped
print "As a decorator maker, I return a decorator"
return my_decorator
>>> def lazy_function():
print "zzzzzzzz"
这里,my_decorator
装饰器作为了decorator_maker
函数的返回值。
>>> new_decorator = decorator_maker()
I make decorators! I am executed only once: when you make me create a decorator.
As a decorator maker, I return a decorator
执行decorator_maker
函数,将装饰器赋值给new_decorator
变量。
接下来就可以和之前一样,创建被装饰函数,并执行它了:
>>> decorated_function = new_decorator(lazy_function)
I am a decorator! I am executed only when you decorate a function.
As the decorator, I return the wrapped function.
>>> decorated_function()
I am the wrapper around the decorated function. I am called when you call the decorated function.
zzzzzzzz
As the wrapper, I return the RESULT of the decorated function.
@
语法:
>>> @new_decorator
def lazy_function_1():
print "zzzzzzzz"
I am a decorator! I am executed only when you decorate a function.
As the decorator, I return the wrapped function.
既然new_decorator
是decorator_maker
函数的返回值,那么再直接一点呢?
>>> @decorator_maker()
def lazy_function_1():
print "zzzzzzzz"
I make decorators! I am executed only once: when you make me create a decorator.
As a decorator maker, I return a decorator
I am a decorator! I am executed only when you decorate a function.
As the decorator, I return the wrapped function.
注意:
- 这里是先执行了装饰器生成函数
decorator_maker
,生成了装饰器,再由装饰器生成了被装饰函数。 - 这里的代码形式和上一节带参数的装饰器报错的代码很像,但这里
@
符号后面代码执行的结果刚好是装饰器函数,所以可以正确运行。那么,带参数的装饰器的正确的姿势,是不是应该也是这样呢?
带参数的装饰器 Beta 2.0
跟着上一节的思路,把参数加入到装饰器生成函数里:
>>> def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2
def my_decorator(func):
print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2
def wrapped(function_arg1, function_arg2):
print ("I am the wrapper around the decorated function.\nI can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function".format(decorator_arg1, decorator_arg2,function_arg1, function_arg2))
return func(function_arg1, function_arg2)
return wrapped
return my_decorator
这里装饰器生成函数接收了两个参数,并且在装饰器生成函数执行时、装饰器函数执行时、被装饰函数执行时,都打印这两个参数,这里可以充分体现出闭包的特性。
再定义一个带装饰函数:
>>> def lazy_function(function_arg1, function_arg2):
print "zzzzzzz"
print "I only knows about my arguments: {0} {1}".format(function_arg1, function_arg2)
这个函数也要接收两个参数,但注意区分下装饰器的参数和待装饰函数的参数,并不一样。
先执行一下装饰器生成函数,创建装饰器:
>>> decorator = decorator_maker_with_arguments("Leonard", "Sheldon")
I make decorators! And I accept arguments: Leonard Sheldon
再执行装饰器,创建被装饰函数:
decorated_function = decorator(lazy_function)
I am the decorator. Somehow you passed me arguments: Leonard Sheldon
最后执行被装饰函数:
>>> decorated_function("Rajesh", "Howard")
I am the wrapper around the decorated function.
I can access all the variables
- from the decorator: Leonard Sheldon
- from the function call: Rajesh Howard
Then I can pass them to the decorated function
zzzzzzz
I only knows about my arguments: Rajesh Howard
还没完,还是得用@
语法来检验一下:
>>> @decorator_maker_with_arguments("Leonard", "Sheldon")
def lazy_function_1(function_arg1, function_arg2):
print "zzzzzzz"
print "I only knows about my arguments: {0} {1}".format(function_arg1, function_arg2)
I make decorators! And I accept arguments: Leonard Sheldon
I am the decorator. Somehow you passed me arguments: Leonard Sheldon
装饰器生成函数运行正常,装饰器运行正常!
再运行下被装饰函数:
>>> lazy_function_1("Rajesh", "Howard")
I am the wrapper around the decorated function.
I can access all the variables
- from the decorator: Leonard Sheldon
- from the function call: Rajesh Howard
Then I can pass them to the decorated function
zzzzzzz
I only knows about my arguments: Rajesh Howard
例子
- flask中检查用户是否具有管理员权限
from functools import wraps
def admin_required(func):
@wraps(func)
def decorated_func(*args, **kwargs):
if not current_user.is_authenticated:
return redirect(url_for('auth.login'))
if current_user.is_admin:
return func(*args, **kwargs)
else:
abort(403)
return decorated_func
- 打印函数执行时间
from functools import wraps
def print_time_cost(func):
@wraps(func)
def decorated_func(*args, **kwargs):
before = datetime.now()
res = func(*args, **kwargs)
after = datetime.now()
print func.__name__ + ': ' + str(after - before)
return res
return decorated_func
非常详细的装饰器讲解