【Python函数式编程】——装饰器

Python函数式编程——装饰器

1.什么是装饰器

先看一段代码:

# -*- coding: utf-8 -*-
# @File  : 什么是装饰器.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 19:38


def fun():
    print('洗脸')


fun()  # 调用fun函数
# 洗脸


# 想给fun函数增加 起床 和 吃早点 这两个功能
def test1(func):
    def test2():
        print('起床')
        func()
        print('吃早点')
    return test2


test1(fun)()
# 起床
# 洗脸
# 吃早点

  由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。也可以将函数赋值变量,做参传入另一个函数。

  装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值 也是一个函数对象。

  它经常用于有以下场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。

2.装饰器的作用

装饰器的作用:为已经存在的对象添加额外的功能

看代码代码:

# -*- coding: utf-8 -*-
# @File  : 装饰器.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 19:46


def test1(func):
    def test2():
        print('起床')
        func()
        print('吃早点')
    return test2


@test1  # 装饰器
def fun():
    print('洗脸')


fun()
# 起床
# 洗脸
# 吃早点

  我们没有直接将fun函数作为参数传入test1中,只是将test1函数以@方式装饰在fun函数上。

  也就是说,被装饰的函数,函数名作为参数,传入到装饰器函数上,不影响fun函数的功能,再此基础上可以根据业务或者功能增加条件或者信息。

(注意:@在装饰器这里是作为Python语法里面的语法糖写法,用来做修饰。)

  但是我们这里就存在一个问题,这里引入魔术方法 __name__ ,这是属于 python 中的内置类属性,它天生就存在于一个 python 程序中,代表对应程序名称,一般一段程序作为主线运行程序时其内置名称就是 __main__ ,当自己作为模块被调用时就是自己的名字。

print(fun.__name__)
# test2

  这并不是我们想要的!输出应该是fun,这里的函数被test2替代了。它重写了我们函数的名字和注释文档,那怎么阻止变化呢,Python提供functools模块里面的wraps函数解决了问题。

代码实现:

# @Time  : 2022/8/21 20:04

from functools import wraps


def test1(func):
    @wraps(func)  # 使用fun来包装test2
    def test2():
        print('起床')
        func()
        print('吃早点')
    return test2


@test1  # 装饰器
def fun():
    print('洗脸')


fun()
print(fun.__name__)  # fun

我们在装饰器函数内,作用funtest2函数上也增加了一个装饰器wraps还是带参数的,这个装饰器的功能就是不改变使用装饰器原有函数的结构。

3.装饰器记录日志功能

work()函数增加记录日志功能

# -*- coding: utf-8 -*-
# @File  : 装饰器实现记录日志功能.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 20:09

import time
from functools import wraps


def log(fun):
    @wraps(fun)
    def write_log():
        print("[info]--时间是:%s" % time.strftime("%Y-%m-%d %H:%M:%S"))
        fun()
    return write_log


@log
def work():
    print("我在学习Python")


work()

4.带参装饰器

  我们也看到装饰器wraps也是带参数的,那我们是不是也可以定义带参数的装饰器呢,我们可以使用一个函数来包裹装饰器,调入这个参数。

# -*- coding: utf-8 -*-
# @File  : 带参数的装饰器.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 21:01

import time
from functools import wraps


def logs(func):
    @wraps(func)
    def write_log(*args, **kwargs):
        print('[info]--时间:%s' % time.strftime('%Y-%m-%d %H:%M:%S'))
        func(*args, **kwargs)
    return write_log


@logs
def work():
    print('我在工作')


@logs
def work2(name1, name2):
    print('%s和%s在工作' % (name1, name2))


work2('张三', '李四')
# [info]--时间:2022-06-24 18:04:04
# 张三和李四在工作


5.函数做装饰器

把日志写入文件

# -*- coding: utf-8 -*-
# @File  : 把日志写入文件.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 21:14

import time
from functools import wraps


def logger(log_file):
    def logs(fun):
        @wraps(fun)
        def write_log(*args, **kwargs):
            log = "[info]--时间是:%s" % time.strftime('%Y-%m-%d %H:%M:%S')
            print(log)
            with open(log_file, 'a+') as f:
                f.write(log+'\n')
            fun(*args, **kwargs)
        return write_log
    return logs


@logger('work.log')  # 使用装饰器来给 work函数增加记录日志的功能
def work(name1, name2):  # 1.当前 work可能有多个参数 2.自定义日志文件的名字和位置,记录日志级别
    print(f"{name1}和{name2}正在工作")


work('张三', '李四')

终端输出:

work.log文件记录日志

  这里我们将带参数的带入进去根据代码流程执行生成了文件并将日志信息打印进去现在我们有了能用于正式环境的logger装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。


6.类做装饰器

# -*- coding: utf-8 -*-
# @File  : 把日志写入文件.py
# @author: Flyme awei 
# @email : Flymeawei@163.com
# @Time  : 2022/8/21 21:14

import time
from functools import wraps


class Log(object):
    def __init__(self, log_file, level="INFO"):
        self.log_file = log_file
        self.level = level

    def __call__(self, fun):  # 定义装饰器,需要有一个接受函数
        @wraps(fun)
        def write_log(*args, **kwargs):
            log = "[%s]--时间是:%s" % (self.level, time.strftime('%Y-%m-%d %H:%M:%S'))
            print(log)
            with open(self.log_file, 'a+') as f:
                f.write(log+'\n')
            fun(*args, **kwargs)
        return write_log


@Log('work3.png')
def learn(name):
    print(f"{name}在学习Python")


@Log('work2.log')  # 使用装饰器来给 work函数增加记录日志的功能
def work(name1, name2):  # 1.当前 work可能有多个参数 2.自定义日志文件的名字和位置,记录日志级别
    print(f"{name1}和{name2}正在工作")


learn("Flyme awei")
work('张三', '李四')

  类实现装饰器有一个优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法。


物联沃分享整理
物联沃-IOTWORD物联网 » 【Python函数式编程】——装饰器

发表评论