python快速学习系列(5):装饰器

装饰器概述:
-理解装饰器要从三方面入手:why?what?how?
-学习装饰器要从模仿开始

1.why?为什么会出现装饰器这个东西?
·名称管理
·显示调用
·就近原则
·充分复用
例如:

def decorate(func):
    func.__doc__ += '\nDecorated by decorate'
    return func

def add(x,y)
    '''Return the sum of x and y.'''
    return x + y

add = decorate(add)

上述写法是没有使用装饰器的写法,改进如下:

def decorate(func):
    func.__doc__ += '\nDecorated by decorate'
    return func

@decorate       #@是一个新的语法。这句话替代了add = decorate(add)
def add(x,y)
    '''Return the sum of x and y.'''
    return x + y

而上述的写法对于小程序已经足够了,但是对于大型程序来说,复用是非常重要的,
可以将所有的装饰器封装到一个类中,然后再调用,例如:
DecorateToolBox.py

class DecorateToolBox:
    def decorate(self,func):
        func.__doc__ += '\nDecorated by decorate'

test.py

from DecorateToolBox import DecorateToolBox
@DecorateToolBox.decorate
def add(x,y):
    '''Return the sum of x and y'''
    return x + y

2.what?什么是装饰器?
·装饰器是一个可调用的对象,以某种方式增强函数的功能(注意是增强而不是修改)
·装饰器是一个语法糖,在源码中标记函数(此源码指编译后的源码)
·解释器解析源码的时候将被装饰的函数作为第一个位置参数传给装饰器
(解释器在解析源码的时候,只要发现了@这个语法糖,就会把下面函数的地址作为第一个参数传给装饰器)
·装饰器可能会直接处理被装饰函数,然后返回它(一般仅修改属性,不修改代码
·装饰器也可能用一个新的函数或可调用对象替换被装饰函数(但核心功能一般不变)
·装饰器仅仅看着像闭包,其实功能的定位与闭包有重合也有很大区别
·装饰器模式的本质是元编程:在运行时改变程序行为
·装饰器的一个不可忽视的特性:在模块加载时立即执行(很特殊)
·装饰器是可以堆叠的,自底向上逐个装饰
·装饰器是可以带参数的,但此时至少要写两个装饰器(可带参数的装饰器一定是闭包)
·装饰器的更加pythonic的实现方式其实是在类中实现__call__()方法

1)装饰器的堆叠

def deco_1(func):
    print('1')
    return func
def deco_2(func):
    print('2')
    return func
@deco_1
@deco_2
def f():
    print('running')
if __name__ == '__main__':
    f()

#结果如下:自下而上
#2
#1
#running

2)装饰器在导入时立即执行:将上述代码照抄,区别在:main里面只是pass

def deco_1(func):
    print('1')
    return func
def deco_2(func):
    print('2')
    return func
@deco_1
@deco_2
def f():
    print('running')
if __name__ == '__main__':
    pass

#运行结果如下:
#2
#1
上述结果说明装饰器在导入时立即执行

3)带参数的装饰器:
既然装饰器只能接受一个位置参数,并且是被动的接受解释器传过来的函数引用,那么如何实现带参数的装饰器呢?
问题分析:
·限制条件一:装饰器本身只能接受一个位置参数
·限制条件二:这个位置参数已经被装饰函数的引用占据了
·问题目标:希望装饰器能够使用外部传入的其他参数
·推论:装饰器需要访问或修改外部参数
三种备选方案:
a)在装饰器内访问全局不可变对象,若需要修改,则使用global声明(不安全)
b)在装饰器内访问外部可变对象(不安全)
c)让装饰器成为闭包的返回(较安全)
方案:编写一个闭包,接受外部参数,返回一个装饰器

registry = set()  #注册表
def register(flage=True):
    def decorate(func):
        if flag:
            registry.add(func)
        else:
            registry.discard(func)
        return func   
        #decorate是一个装饰器,因为它**输入一个函数,返回一个函数**,而且在中间代码里没有对原函数进行逻辑上的更改
    return decorate

@register()   #一个装饰器后面带不带(),直接决定了它仅仅是一个装饰器还是一个闭包
def f1():
    print('running f1')

@register(False)
def f2():
    print('running f2')

@register(True)
def f3():
    print('running f3')

def main():
    f1()
    f2()
    f3()

if __name__ == '__main__':
    print(registry)
    main()

结果如下:
{<function f1 at 内存地址>,<function f3 at 内存地址>}
running f1
running f2
running f3
分析:
*此时,register变量被使用了两次
*第一次是后面的调用:() (调用之后才变成一个装饰器,注意要把()看成调用)
*第二次是前面的装饰:@ (装饰器符合仅能用于装饰器)
注意:register不是装饰器,register()或register(False)才是

3.How?装饰器怎么用?
装饰器的常见使用场景
·运行前处理:如确认用户授权
·运行时注册:如注册信号系统
·运行后清理:如序列化返回值

1)注册机制or授权机制(往往和应用开发相关)
*函数的注册,参考上面的例子
*将某个功能注册到某个地方,比如Flask框架中URL的注册
*比如验证身份信息或加密信息,以确定是否继续后续的运算或操作
*比如查询一个身份信息是否已经被注册过,如果没有则注册,如果有则直接返回账户信息

2)参数的数据验证或清洗(往往跟数据清洗或异常处理相关)
等等

总结:
·装饰器作为python四大神器之一,功能非常强大(还有迭代器,生成器,上下文管理器)
·装饰器用好了能让程序复用性大大提高,代码也会变得非常优雅
·要理解装饰器,需要先理解一等函数,变量作用域,函数式编程,闭包
·要用好装饰器,要从模仿开始,多读别人的代码,多模仿,慢慢就有感觉了
·python内置了很多的装饰器,功能都很强大,辩词额框架或大型程序的时候一定会用到

猜你喜欢

转载自blog.csdn.net/shanlepu6038/article/details/84671311
今日推荐