Python 装饰器
装饰器 decorator
装饰器是函数,通过在被装饰的函数前添加@func 来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作。
作用: 给已经实现的功能扩展新的功能。装饰器在web框架里常用.
例如我们网站有前台和后台,前台默认不需要登录就能访问,后台需要用户验证登录才能进入,源代码如下,但我们不希望修改源码,实现验证登录后台
#!/usr/bin/env python def home(): print('home page') def admin(): print('admin page')
admin()
方法一:定义一个新的函数login()用于用户验证,接受一个函数作为参数,并返回这个函数。
#!/usr/bin/env python
def login(func): PASS=input('Input passwd: ') if PASS=='123': print('Verify through') return func def home(): print('home page') def admin(): print('admin page') admin=login(admin) # 这里相当于重新定义了admin函数 admin()
方法二: 应用装饰器
#!/usr/bin/env python
def login(func): PASS=input('Input passwd: ') if PASS=='123': print('Verify through') return func def home(): print('home page') @login def admin(): print('admin page') admin() 运行结果: Input passwd: 123 Verify through admin page
实际上@login等同于admin=login(admin),只是用了python的@符号换了一种简单的表达方式而已,这就是装饰器,装饰了函数admin
但是以上代码是有问题的,我们注释掉admin(), 发现没有访问后台(即调用admin函数)也需要我们输入验证,因此这样是不合理的。事实上@login已经执行了login函数,我们进一步优化,只有在调用admin函数时情况下才弹出验证窗口, 因此我们在login内再封装一层函数,如下:
def login(func): def wrapper(): PASS=input('Input passwd: ') if PASS=='123': print('Verify through') return func() return wrapper
这样python解释器在执行到@login 时将返回一个wrapper函数,现在admin就等于wrapper,不再是原来的admin,执行admin时就是执行wrapper,可以用print(admin.__name__) 看到,需要把原始函数的__name__
等属性复制到wrapper()
函数中,则需要在定义wrapper()
的前面加上@functools.wraps(func),如下:
#!/usr/bin/env python import functools # 导入functools模块 def login(func): @functools.wraps(func) def wrapper(*args,**kwargs): PASS=input('Input passwd: ') if PASS=='123': print('Verify through') return func(*args,**kwargs) return wrapper def home(): print('home page') @login def admin(name): print('admin page') print(admin.__name__) admin('Zhou')
运行结果:
admin
Input passwd: 123
Verify through
admin pag
如果被装饰的函数需要传递参数,如用户名,则装饰器定义如下:
#!/usr/bin/env pythondef login(func): def wrapper(*args,**kwargs): PASS=input('Input passwd: ') if PASS=='123': print('Verify through') return func(*args,**kwargs) return wrapper def home(): print('home page') @login def admin(name):print('admin page') admin('Zhou')
运行结果:
Input passwd: 123
Verify through
admin page
因此定义一个正确的装饰器一般都是两层函数,定义起来虽然有点复杂,但使用起来非常灵活和方便。
带参数的装饰器
装饰器本身就是函数,因此它可以接受参数。
继续上面的例子,假如现在有新的需求,要求支持多种方式登录(如密码登录,扫码登录或密钥登录等),登录成功则进行其他操作,反之return, 返回页面后可以有后续的操作。但我们不希望定义多个装饰器,因为定义装饰器的成本较大(每个装饰都需要嵌套一层函数),我们在原装饰器的基础上再封装一层函数,而将新的需求做成函数作为参数传递给装饰器,如下所示:
#!/usr/bin/env python def Filter(Before_func,After_func): def login(func): def wrapper(*args,**kwargs): Before_func(*args,**kwargs) func(*args,**kwargs) After_func(*args,**kwargs) return wrapper return login def home(): print('home page') def Before(*args): print('before') # 这里用print代替验证方法 def After(*args): print('after') @Filter(Before,After) def admin(user): print('admin page')
admin('Zhou')
运行结果:
before
admin page
after
可以理解被装饰的函数是装饰器的默认参数
@Filter(Before,After)
相当于
admin=login(Before,After)(admin)
或
分成了两步执行:1. Filter(Before,After),返回login 2. @login
后言:在运维基础平台,我们常常需要提供一些接口给开发的同事,对于开发人员来讲这个接口是透明的,在完成对他们新的需求后调用接口的方式不变。此时若我们不想改源码,可以用装饰器来实现。