2021-07-05 Python装饰器

之前总觉得装饰器好难理解,静下心来研究了一番,其实也还算简单。

我觉得他的作用(好处):

1、不改变函数调用方式的情况下,增加函数功能,例如日志记录、认证、授权检查等。
2、代码复用,装饰器本身也是一个函数,一个功能多次调用
3、代码看起来更牛逼!

装饰器最基本使用方法

1、我们有一个最简单的打印函数,想要在不改变函数调用的情况下增加记录日志的功能:

def func_print():
    print('这就是个简单的打印函数,打印这句话')

if __name__=="__main__":
    func_print()

2、我们需要构造一个装饰器函数来增加日志功能
我的理解:
装饰器就是函数的多层嵌套(最基本的使用方法就是两层函数嵌套),装饰器在装饰函数时,外层函数先被调用,外层函数定义了一个内层函数,装饰器外层函数return值为内层函数的函数名(C语言里面的指针比较好理解,就是内层程序的执行入口)
实现效果:装饰器内层函数在原函数的基础上添加了新的功能,通过外层函数的调用,返回内层函数的执行入口,调用原函数的时候实际上是调用了名字一样的装饰器内层函数
@Decorator_Logging的作用== func_print=Decorator_Logging(func_print)
Decorator_Logging(func_print)的返回值是内层函数的执行入口(函数名)。
举个例子:

#构造装饰器函数,传入形参为函数
def Decorator_Logging(func):
    #函数里面定义函数,用来作为上一层函数的返回值,可以理解为:调用外层函数的时候,返回内层函数的‘指针’
    def wrapper_func():
        print('save log to some file:***.log ')
        #原函数不变,将在内层函数被调用的时候执行
        func()
        print('log saved to file:***.log ')
    #返回内层函数执行入口
    return wrapper_func

#装饰器的调用方式,@+函数名 ,作用上= func_print=Decorator_Logging(func_print)
@Decorator_Logging
def func_print():
    print('这就是个简单的打印函数,打印这句话')
if __name__=="__main__":
    func_print()

输出:

save log to some file:***.log 
这就是个简单的打印函数,打印这句话
log saved to file:***.log 

看似主程序里面调用的还是原来的func_print(),实际上早已被装饰器偷天换日。(这里说的比较简单,想更深层次理解装饰器,建议搜索下‘Python 闭包’)

装饰器传入参数

以上只是装饰器最简单的应用,当你写的函数需要传参数的时候怎么使用装饰器?
最简单的理解,原函数有参数,最终执行的时候执行的是装饰器的内层函数,那么应该保持原函数和装饰器的内层参数一致

def Decorator_Logging(func):
  #传入参数
    def wrapper_func(some_str):
        print('save log to some file:***.log ')
        func(some_str)
        print('log saved to file:***.log ')
    return wrapper_func

@Decorator_Logging
def func_print(some_str):
    print('这就是个简单的打印函数,打印这句话%s'%(some_str))
if __name__=="__main__":
    func_print('我爱你')

输出:

save log to some file:***.log 
这就是个简单的打印函数,打印这句话我爱你
log saved to file:***.log 

不定参数

装饰器另一个功能也是为了复用,如果有其它函数也需要用日志的装饰器,但是函数和现在的函数参数数量内容都不一样该怎么办?
使用不定参数解决,装饰器返回值为函数入口,此时内层函数并未被调用,内层函数的参数也未被确定,为了扩展,我们就让它支持任意形式参数
args和*kwargs的用法就不再单独介绍了,直接上实例:

def Decorator_Logging(func):
    #传入不定参数
    def wrapper_func(*args,**kwargs):
        print('save log to some file:***.log ')
        #传入不定参数
        func(*args,**kwargs)
        print('log saved to file:***.log ')
    return wrapper_func

@Decorator_Logging
def func_print(some_str):
    print('这就是个简单的打印函数,打印这句话%s'%(some_str))

@Decorator_Logging
def func_print2(some_str1,some_str2):
    print('这是第二个简单的打印函数,打印这句话%s,%s'%(some_str1,some_str2))

if __name__=="__main__":
    func_print('我爱你')
    func_print2('我爱你','那么爱你!')

输出:

save log to some file:***.log 
这就是个简单的打印函数,打印这句话我爱你
log saved to file:***.log 
save log to some file:***.log 
这是第二个简单的打印函数,打印这句话我爱你,那么爱你!
log saved to file:***.log 

返回值的处理

如果定义的函数带有返回值该如何处理?
一样的思考,原函数有返回值,让装饰器内层函数也有返回值不就可以了?!!!so easy!!!
(未在函数中用return明确指出返回值的,函数默认返回None)
上示例:

#构造装饰器函数,传入形参为函数
def Decorator_Logging(func):
    #传入不定参数
    def wrapper_func(*args,**kwargs):
        print('save log to some file:***.log ')
        #传入不定参数
        func_result=func(*args,**kwargs)
        print('log saved to file:***.log ')
        #内层函数的返回值
        return func_result
    return wrapper_func

@Decorator_Logging
def func_print(some_str):
    return ('这就是个简单的打印函数,打印这句话%s'%(some_str))
@Decorator_Logging
def func_print2(some_str1,some_str2):
    return ('这是第二个简单的打印函数,打印这句话%s,%s'%(some_str1,some_str2))
if __name__=="__main__":
    print(func_print('我爱你'))
    print(func_print2('我爱你','那么爱你!'))

输出:

save log to some file:***.log 
log saved to file:***.log 
这就是个简单的打印函数,打印这句话我爱你
save log to some file:***.log 
log saved to file:***.log 
这是第二个简单的打印函数,打印这句话我爱你,那么爱你!

多个装饰器同时使用

多个装饰器是可以同时使用的,至于多个装饰器使用时候内层函数的调用顺序(实际上是一个装饰器调用另一个装饰器,嵌套),可以自己加个print测试一下,仅给出示例:

#定义一个装饰器函数(功能为记录日志)
def Decorator_Logging(func):
    #*args,**kwargs 用于匹配被装饰函数需要传参的情况
    def wrapper_func(*args,**kwargs):
        print('save log to some file:***.log ')
        #将参数传入被装饰函数,仅在函数被调用的时候执行
        result_of_func=func(*args,**kwargs)
        print('log saved to file:***.log ')
        #return 用于被装饰函数存在‘return’的情况
        return result_of_func
    #返回函数
    return wrapper_func
#定义一个装饰器函数(功能为添加try、except)
def Decorator_Exception(func):
    def wrapper_func(*args,**kwargs):
        try:
            print('已经进入try模块:')
            result_of_func=func(*args,**kwargs)
            print('try模块正常结束')
            return result_of_func
        except Exception as e:
            print('程序有错误:')
            print(e)

    return wrapper_func
@Decorator_Exception
@Decorator_Logging
def test_func(a,b,c):
    print('this is a sum func,result is :',a+b+c)

if __name__=="__main__":
    test_func(1,2,3)

输出:

已经进入try模块:
save log to some file:***.log 
this is a sum func,result is : 6
log saved to file:***.log 
try模块正常结束

通过结果可以看出是上面的装饰器把下面的装饰器又嵌套了。
就像:第一个装饰器(第二个装饰器(原函数))

你可能感兴趣的:(2021-07-05 Python装饰器)