python打卡day27

函数装饰器
知识点回顾:

  1. 装饰器的思想:进一步复用
  2. 函数的装饰器写法
  3. 注意内部函数的返回值

日常ctrl点进某个复杂的项目,发现函数定义上方有一个@xxx,它就是装饰器。装饰器本质上是一个 Python 函数,可以在不修改原函数代码的情况下,给函数添加额外功能。本质是如果让一个函数具备太多功能,那么他看起来就会比较乱,可读性比较差,如果把其中一部分相同甚至可以复用的功能用一个新的函数来调用,然后让2个函数同时实现,就会做到:

  • 进一步封装了函数的一些用法,做到dry原则(don't repeat yourself)
  • 使函数更加具有可读性

所以装饰器本身就是函数中调用其他函数,实现先拆分函数,再合并函数的功能

举个例子,假设你有一个函数 prime_nums() ,核心功能是筛选2-10000的质数并输出:

python 复制代码
def is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True

def prime_nums():
    for i in range(2, 10000):
        if is_prime(i):
            print(i)

prime_nums()

现在要加入计算耗时的功能,一般会这样做:

python 复制代码
import time
def is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


def prime_nums():
    t1 = time.time()
    for i in range(2, 10000):
        if is_prime(i):
            print(i)
    t2 = time.time()
    print(f"执行时间:{t2 - t1}秒")

prime_nums()

但如果使用函数装饰器:

python 复制代码
# 定义一个装饰器
def display_time(func): # 装饰器函数,接收一个函数func作为参数
    def wrapper(): # 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的
        t1 = time.time()
        func()  # 直接调用原函数(无参数),这里的func()是指装饰器需要修饰的函数,在这里是prime_nums()
        t2 = time.time()
        print(f"执行时间: {t2 - t1} 秒")
    return wrapper # return wrapper是返回函数对象,如果是return wrapper()则是立即执行wrapper函数

def is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


@display_time # 相当于 prime_nums = display_time(prime_nums)
def prime_nums():
    for i in range(2, 10000):
        if is_prime(i):
            print(i)

prime_nums()

装饰函数就是在原函数的基础上套了个壳子wrapper() ,这个壳子里不仅有原函数,还有额外功能的函数实现:

✅ 外层的装饰器display_time函数:确实只是"取名+接收原函数"的入口

✅ wrapper函数:真正的"功能增强实现层",不可或缺

如果原函数需要传入参数并且还要有返回值(比如传入参数maxnum,计算2-maxnum的质数的个数),那么定义装饰器函数的时候也要传入参数和传递返回值:

python 复制代码
def display_time(func):
    def wrapper(*args, **kwargs):  # 接收任意参数
        start = time.time()
        result = func(*args, **kwargs)  # 执行原函数并保存返回值
        end = time.time()
        print(f"耗时: {end-start}秒")
        return result  # 返回原函数的结果
    return wrapper

这里提一下两个返回 return result 和 return wrapper :

  • return wrapper 属于外层display_time函数,在装饰器被应用时执行,返回包装后的函数对象
  • return result 属于内层wrapper函数,在被装饰的原函数每次调用时执行,返回原函数的计算结果

作业:

编写一个装饰器 logger,在函数执行前后打印日志信息(如函数名、参数、返回值)

python 复制代码
def logger(func):
    def wrapper(*args, **kwargs):
        print(f'执行函数:{func.__name__},参数为:{args},{kwargs}') # {}内不能使用解包操作符*
        result = func(*args, **kwargs)
        print(f'函数已执行,返回结果:{result}')
        return result
    return wrapper

@logger
def calculate_average(*args, **kwargs):
    if not args:
        return 0
   
    try:
        average = sum(args) / len(args) # 这里不用*是因为元组本身就是这两个函数的可迭代对象,
                                        # 比如sum((1,2,3))合法,但sum(1,2,3)不行,因为sum()只接受一个可迭代参数
        return average
    except TypeError:
        print("错误:所有参数必须是数字")
        return 0

print(f'[4, 8, 6.5, 15]的均值为:{calculate_average(4, 8, 6.5, 15)}')
print(f'空输入的均值为:{calculate_average()}')
print(f'非数字输入的均值为:{calculate_average("a", "b")}')
XML 复制代码
执行函数:calculate_average,参数为:(4, 8, 6.5, 15),{}
函数已执行,返回结果:8.375
[4, 8, 6.5, 15]的均值为:8.375
执行函数:calculate_average,参数为:(),{}
函数已执行,返回结果:0
空输入的均值为:0
执行函数:calculate_average,参数为:('a', 'b'),{}
错误:所有参数必须是数字
函数已执行,返回结果:0
非数字输入的均值为:0

收获心得:

函数装饰器真的是第一次接触,新奇

今天终于把昨天讲的 *args 和 **kwargs 这种什么时候解包(加上*)什么时候不解包(不加*)搞清楚了,只在需要将元组或字典拆分为独立参数时才使用*args或**kwargs,其他情况直接使用元组或字典即可

@浙大疏锦行

相关推荐
xiaoye-duck10 分钟前
《C++ 项目:手写大模型接入 SDK》DeepSeek接口设计(上):初始化和全量响应
开发语言·网络·c++·语言模型
fpcc15 分钟前
跟我学c++中级篇—streambuf在glog中的应用分析
开发语言·c++
吾与谁归in17 分钟前
C#脚本工具,支持智能提示自动补全(WPF + Roslyn + AvalonEdit)
开发语言·c#·wpf
超级架构师19 分钟前
让 Fortran 模型自己调用 Agent:原生 Fortran 2008 实现深度剖析
人工智能·python·架构·机器人·开源·fortran
小蒜学长23 分钟前
基于Django+Vue喵汪联盟宠物领养系统的设计与实现(代码+数据库+LW)
后端·python·django·宠物领养系统·流程规范化
昂好的day28 分钟前
WPF 从零入门(上):WPF 是什么、XAML 语法、布局与样式全解
开发语言·c#·wpf
飞浪纪元[FWC–FE]31 分钟前
Qt 构建排错实录:从 MSB 1009 到生成器选型
开发语言·数据库·qt
fly-iot32 分钟前
【FDE知识库】(1):AI 编程时代开发语言选型分析,云使用go,企业用java,算法python,底层c
开发语言·人工智能·golang
云边有个稻草人32 分钟前
Prometheus Pushgateway教程:离线任务指标上报、Shell与Python示例、远程访问
开发语言·python·prometheus
暴走约伯34 分钟前
【VFX工具链源码拆解 01】总体架构:大型项目特效流程的程序化落地
开发语言·经验分享·3d·unity·架构·游戏引擎