云计算百科
云计算领域专业知识百科平台

Python进阶必杀技——闭包三要素与装饰器完全指南

前言

如果你已经熟悉了函数的基本用法,那么恭喜你来到Python进阶的大门!闭包(Closure)和装饰器(Decorator)是Python函数式编程的精髓,也是面试中绕不开的高频考点。本文将深入剖析闭包的“三要素”,并带你手写通用的日志装饰器、计时装饰器,让你真正理解@语法糖背后的原理。


1. 闭包(Closure)—— 函数工厂的基石

1.1 什么是闭包?

闭包本质上是一个嵌套函数,它让内层函数捕获并记住了外层函数的局部变量,即使外层函数已经执行完毕。

1.2 闭包三要素(面试必背)

  • 函数嵌套:内层函数定义在外层函数内部。

  • 内层函数使用外层函数的变量(捕获状态)。

  • 外层函数返回内层函数(把“行为”返回出去)。

  • 1.3 经典案例:计数器工厂

    def counter(step):
    count = 0 # 外部局部变量
    def inner():
    nonlocal count # 声明修改外部变量
    count += step
    return count
    return inner

    # 创建两个独立的计数器实例
    c1 = counter(1)
    c2 = counter(2)

    print(c1()) # 1
    print(c1()) # 2
    print(c2()) # 2
    print(c2()) # 4

    每次调用counter,都会生成一个新的闭包空间,c1和c2的count互不干扰。


    2. 装饰器(Decorator)—— 不修改代码,只增强功能

    2.1 装饰器的本质

    装饰器就是一个闭包,它接收一个函数作为参数,返回一个增强后的新函数。
    语法糖 @deco 等价于 func = deco(func)。

    2.2 第一个计时装饰器

    在不改动原函数的情况下,计算函数执行时间:

    import time

    def timer(func):
    def wrapper():
    start = time.time()
    func()
    print(f"函数 {func.__name__} 耗时: {time.time() – start:.4f}s")
    return wrapper

    @timer
    def sleep_func():
    time.sleep(1)

    sleep_func() # 函数 sleep_func 耗时: 1.0002s

    2.3 通用装饰器(支持参数和返回值)

    实际开发中,原函数往往有参数和返回值。我们需要用 *args, **kwargs 通吃一切。

    def universal_deco(func):
    def wrapper(*args, **kwargs):
    print(f"正在执行 {func.__name__}")
    result = func(*args, **kwargs) # 接收返回值
    print("执行结束")
    return result # 返回返回值
    return wrapper

    @universal_deco
    def add(a, b):
    return a + b

    print(add(5, 3)) # 8 (同时打印了日志)


    3. 多个装饰器叠加(执行顺序)

    当多个装饰器叠在一起时,从下往上包装,调用时从上往下执行。

    def deco1(func):
    def wrapper():
    print("开始装饰1")
    func()
    print("结束装饰1")
    return wrapper

    def deco2(func):
    def wrapper():
    print("开始装饰2")
    func()
    print("结束装饰2")
    return wrapper

    @deco1
    @deco2
    def core():
    print("核心业务")

    core()
    # 输出顺序:
    # 开始装饰1
    # 开始装饰2
    # 核心业务
    # 结束装饰2
    # 结束装饰1


    4. 实战案例(一):排序算法时间开销统计

    我们生成10000个随机数,对比 sorted()、list.sort()、冒泡排序、选择排序的效率。利用装饰器,零侵入地统计每个算法耗时。

    import random
    import time

    # 准备4份相同的数据
    data = [random.randint(1, 100000) for _ in range(10000)]
    d1, d2, d3, d4 = data.copy(), data.copy(), data.copy(), data.copy()

    def time_calc(func):
    def wrapper():
    start = time.time()
    func()
    print(f"{func.__name__} 耗时: {time.time() – start:.4f}s")
    return wrapper

    @time_calc
    def test_sorted():
    sorted(d1)

    @time_calc
    def test_sort():
    d2.sort()

    @time_calc
    def test_bubble():
    # 冒泡排序(优化版)
    for i in range(len(d3)-1):
    for j in range(len(d3)-i-1):
    if d3[j] > d3[j+1]:
    d3[j], d3[j+1] = d3[j+1], d3[j]

    @time_calc
    def test_selection():
    # 选择排序
    for i in range(len(d4)-1):
    min_idx = i
    for j in range(i+1, len(d4)):
    if d4[j] < d4[min_idx]:
    min_idx = j
    if min_idx != i:
    d4[i], d4[min_idx] = d4[min_idx], d4[i]

    test_sorted() # 极快
    test_sort() # 极快
    test_bubble() # 龟速(慎用,可能要几十秒)
    test_selection() # 较慢

    运行后你会直观感受到算法时间复杂度带来的性能差异,这正是装饰器在性能监控中的典型应用。


    5. 实战案例(二):日志记录装饰器(保存到文件)

    利用 datetime 记录函数执行的时间、参数和返回值,并写入TXT日志。

    from datetime import datetime

    def logger(func):
    def wrapper(*args, **kwargs):
    result = func(*args, **kwargs)
    # 拼接日志内容
    log_msg = (f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | "
    f"函数: {func.__name__} | "
    f"参数: {args} {kwargs} | "
    f"返回值: {result}\\n")
    # 写入文件(追加模式)
    with open("func_log.txt", "a", encoding="utf-8") as f:
    f.write(log_msg)
    return result
    return wrapper

    @logger
    def max_value(a, b):
    return a if a > b else b

    max_value(10, 20)
    max_value(100, 50)

    打开同目录下的 func_log.txt,你会看到清晰的调用记录。


    6. 装饰器带参数(三层嵌套)

    有时我们希望装饰器本身能接收参数(如指定日志文件名),需要再包一层。

    def file_logger(filename):
    def decorator(func):
    def wrapper(*args, **kwargs):
    result = func(*args, **kwargs)
    with open(filename, "a") as f:
    f.write(f"{func.__name__} 执行完毕\\n")
    return result
    return wrapper
    return decorator

    @file_logger("app.log")
    def run_task():
    print("任务进行中…")

    run_task()

    这种“装饰器工厂”写法在实际框架(如Flask路由)中非常常见。


    结语

    闭包和装饰器是Python进阶路上绕不开的两座大山。理解闭包的三要素,你就能看懂一切装饰器的源码;掌握装饰器的嵌套机制,你就能写出高可用的日志、权限、缓存、计时等通用组件。

    建议你亲手把文中的代码敲一遍,观察控制台输出和文件写入结果。只有动手实践,才能真正内化这些知识!

    如果这篇文章帮到了你,欢迎一键三连,我们下期见!

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Python进阶必杀技——闭包三要素与装饰器完全指南
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!