最近学习python,其中decorator比较难理解,遂写一篇来总结供后续查阅。
定义一个函数,想在运行时动态的改变函数的功能,又不想改变函数本身的代码,可以使用高阶函数(可以使用函数作为参数)
装饰器函数可以简化代码,避免每个函数编写重复的代码,也可以用在python web开发中进行登录限制。
1,一般的函数调用-硬编码调用,代码中直接调用函数(直接调用原函数):
def func1():
print ("func1") def func2():
print("before")
func1()
print("after") func2()
输出为:
2,python中可将函数作为参数进行传递(高阶函数):
def func1():
print("func1") def wrapper(func): #装饰器函数
print("before")
func()
print("after")
return func #如果不写return func,函数会自动返回,默认为0 func1=wrapper(func1)
调用结果同上。
wrapper函数最后如果不返回func,再次调用func1,则会包以下错误TypeError: 'NoneType' object is not callable:
3,python中的decorator中可以简化上述调用:
def wrapper(func):
print("before")
func()
print("after")
return func @wrapper
def func1():
print("func1")
直接执行,结果同上。
4,这种装饰器执行一次调用一次,我们如果希望显示调用,则可以在函数内部封装一层:
def wrapper(func):
def inner():
print("before")
func()
print("after")
return inner @wrapper
def f():
print("call f") f()#显示调用
5,上面是无参数的装饰器,python中可以实现有参装饰器调用:
def wrapper(func):
def inner(a,b):
print("before wrapper")
func(a,b)
print("after wrapper")
return inner @wrapper
def f(a,b):
print ("call f: a+b=%d"% (a+b) ) f(2,3) # f=wrapper(f)=inner
6,有时候,不确定装饰器中参数个数的情况下,就不能使用一般参数、默认参数了,可以使用(*args, **kwargs)来自动适应变参和命名参数:
#coding:utf-8
def wrapper(func):
def inner(*args, **kwargs):
print("before %s"%func.__name__)
result=func(*args,**kwargs)
print("result of %s is %d"%(func.__name__,result))
return inner @wrapper
def f1(a,b):
print("call f1")
return a+b
@wrapper
def f2(a,b,c):
print("call f2")
return a+b+c f1(1,2)
f2(2,3,4)
7,使用functools.wraps在装饰器中包裹传进来的函数,这样就不会丢失传进来的函数的__name__等属性:
from functools import wraps def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('wrapper')
func(*args, **kwargs) return wrapper @my_decorator
def my_fun():
print('test') #exec
my_fun()
8,装饰器在flask中的应用:
#只贴出装饰器核心代码
def login_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if session.get('manage_name'):
return func(*args, **kwargs)
else:
return redirect(url_for('login')) return wrapper @app.route('/post')
@login_decorator
def post():
if request.method == 'GET':
return render_template('post.html')
else:
pass
具体代码可见我的github
参考:简书