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

后言:在运维基础平台,我们常常需要提供一些接口给开发的同事,对于开发人员来讲这个接口是透明的,在完成对他们新的需求后调用接口的方式不变。此时若我们不想改源码,可以用装饰器来实现。

posted @ 2017-05-31 16:29  bobo0609  Views(187)  Comments(0Edit  收藏  举报