【新人系列】Python 入门(十九):装饰器 - 上

发布于:2025-02-11 ⋅ 阅读:(70) ⋅ 点赞:(0)

✍ 个人博客:https://blog.csdn.net/Newin2020?type=blog
📝 专栏地址:https://blog.csdn.net/newin2020/category_12801353.html
📣 专栏定位:为 0 基础刚入门 Python 的小伙伴提供详细的讲解,也欢迎大佬们一起交流~
📚 专栏简介:在这个专栏,我将带着大家从 0 开始入门 Python 的学习。在这个 Python 的新人系列专栏下,将会总结 Python 入门基础的一些知识点,方便大家快速入门学习~
❤️ 如果有收获的话,欢迎点赞 👍 收藏 📁 关注,您的支持就是我创作的最大动力 💪

1. 闭包

可以将闭包理解为一种特殊的函数,这种函数由两个函数的嵌套组成,且称之为外函数和内函数,外函数返回值是内函数的引用,此时就构成了闭包。

闭包格式:

def 外层函数(参数):
    def 内层函数():
        print("内层函数执行", 参数)
        
    return 内层函数

内层函数的引用 = 外层函数("传入参数")
内层函数的引用()

举个例子:

def print_msg():
    msg = "I'm 张三"
    # printer是嵌套函数
    def printer():
        print(msg)
    return printer

prt = print_msg()
prt()

2. 装饰器使用

2.1 基本介绍

装饰器(Decorator)是一种用于修改或增强函数或类的功能的工具。

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

有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

import logging
logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

def use_logging(func):
    def wrapper():
        logging.info("%s is running" % func.__name__)
        func()  # 把func_a当做参数传递进来,执行func()就相当于执行func_a()
    return wrapper

def func_a():
    print("i am func_a")

func_a = use_logging(func_a)  # 装饰器use_logging(func_a)返回的是函数对象wrapper
func_a()    # 执行func_a()相当于执行wrapper()

在这里插入图片描述

2.2 语法糖

上面例子的这种写法稍微有些繁琐,一般而言我们可以使用装饰器提供的 @ 语法糖(Syntatic Suger)来修饰其它函数或对象。

2.2.1 不带参数

以下代码是在上面案例基础上增加了语法糖的使用:

使用语法糖后可以发现就不用我们手动的传入 func_a 函数,而是通过语法糖帮我们将 func_a 自动传递到 use_logging 函数中。

import logging
logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

def use_logging(func):
    def wrapper():
        print("test1")
        logging.info("%s is running" % func.__name__)
        func()  # 把func_a当做参数传递进来,执行func()就相当于执行func_a()
        print("test2")
    return wrapper

@use_logging    # 添加这个相当于执行了func_a=use_logging(func_a)
def func_a():
    print("i am func_a")

func_a()

在这里插入图片描述

2.2.2 传入函数带参数

我们还是拿上面的例子举例,如果想在 func_a 传入参数,则也需要在 wrapper 处接受传入的参数,因为最终调用 func_a 时,实际调用的是 wrapper 函数。

import logging
logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

def use_logging(func):
    def wrapper(name):
        print("test1")
        logging.info("%s is running" % func.__name__)
        func(name)  # 把func_a当做参数传递进来,执行func()就相当于执行func_a()
        print("test2")
    return wrapper

@use_logging    # 添加这个相当于执行了func_a=use_logging(func_a)
def func_a(name):
    print("i am func_a", name)

func_a("张三")

另外,如果传入的参数可能很多,或者数量是不确定,则可以使用可变参数进行接受。

import logging
logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

def use_logging(func):
    def wrapper(*args, **kwargs):
        print("test1")
        logging.info("%s is running" % func.__name__)
        func(*args, **kwargs)  # 把func_a当做参数传递进来,执行func()就相当于执行func_a()
        print("test2")
    return wrapper

@use_logging    # 添加这个相当于执行了func_a=use_logging(func_a)
def func_a(name):
    print("i am func_a", name)

func_a("张三")

再举个常见的例子,我们可以使用装饰器来计算函数执行的时间,如下所示。

import time

def timer(func):
    def wrapper():
        start_time = time.time()
        result = func()
        end_time = time.time()
        print(f"函数 {func.__name__} 执行时间: {end_time - start_time} 秒")    # 函数 my_function 执行时间: 2.0055439472198486 秒
        return result
    return wrapper

@timer
def my_function():
    time.sleep(2)
    print("函数执行完毕")

my_function()

2.2.3 装饰器带参数

如果装饰器也带参数,即我让上面的装饰器来根据不同的条件记录不同级别的日志,则写法又不一样了,需要再封装一层装饰器。

import logging
logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warning("%s is running by warning" % func.__name__)
            elif level == "info":
                logging.info("%s is running by info" % func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@use_logging(level="warn")
def func_a(*args, **kwargs):
    print("i am %s %s" % (args, kwargs))

@use_logging(level="info")
def func_b(*args, **kwargs):
    print("i am %s %s" % (args, kwargs))

func_a(18, 20, name="张三")
func_b(20, name="李四", age=18)

在这里插入图片描述

2.3 应用场景

装饰器可以用于实现很多功能,比如日志记录、函数执行时间统计、执行函数前预备处理、执行函数后清理功能、权限校验、缓存结果、参数验证等等。装饰器还可以多层嵌套,也可以用于装饰类。

举个例子,我现在一个模块下存在很多功能函数例如 f1、f2、f3。现在我想给这些功能都加上一个鉴权的代码,我们如果将鉴权代码或者鉴权函数一个个加到 f1、f2、f3 中会很麻烦,并且最重要的是会破坏开放封闭原则(即当需要增加新的功能时,应该通过扩展现有代码来实现,而不是去修改已有的代码)。

因此,我们就可以使用装饰器来扩展鉴权的功能,从而不会破坏开放封闭原则。

# 鉴权函数
def f():
    # 验证1
    # 验证2
    # 验证3
    pass

def check_wrapper(f):
    def wrapper():
        # 验证逻辑
        f()
    return wrapper()

@check_wrapper
def f1():
    print("f1")

@check_wrapper
def f2():
    print("f2")

@check_wrapper
def f3():
    print("f3")

网站公告

今日签到

点亮在社区的每一天
去签到