Python 装饰器(Decorators)

Python 装饰器【函数闭包(function closure)的语法糖(Syntactic sugar)】

1. 函数闭包(function closure)

多个函数耦合

import time
# 函数逻辑(查找奇数)和辅助功能(记录时间)耦合在一起了,
# 缺点:不方便修改,容易引起bug
# 能不能将辅助功能从主要功能函数中抽离出来?
def print_odds():
# 输出0~100之间所有奇数,并统计函数执行时间
    start_time = time.clock()
    #起始时间
    #查找并输出所有奇数
    for i in range(100):
        if i % 2 == 1:
            print(i)
    end_time = time.clock()
    #结束时间
    print("it takes {} s to find all the odds".format(end_time - start_time))
if __name__ == '__main__':
    print_odds()

解耦

import time
# 解耦,函数职责分离
# 缺点:要通过辅助函数来调用主要功能函数,不方便
def count_time(func):
    start_time = time.clock()
    func()
    end_time = time.clock()
    # 结束时间
    print("it takes {} s to find all the odds".format(end_time - start_time))

def print_odds():
    # 输出0~100之间所有奇数,并统计函数执行时间
    # 查找并输出所有奇数
    for i in range(100):
        if i % 2 == 1:
            print(i)

if __name__ == '__main__':
    count_time(print_odds)

对应使用者来说应该只知道主要函数,辅助函数应该隐藏

函数闭包:一个函数,其参数和返回值都是函数

  • 用于增强函数功能
  • 面向切面编程(AOP)
import time
def print_odds():
    # 输出0~100之间所有奇数,并统计函数执行时间
    # 查找并输出所有奇数
    for i in range(100):
        if i % 2 == 1:
            print(i)

# 闭包本质上是一个函数
# 闭包函数的传入参数和返回值也都是函数
# 闭包函数的返回函数是对传入函数进行增强后的结果
def count_time_wrapper(func):
    # 闭包
    # 用于增强函数 func:给函数 func 增加统计时间的功能
    def improve_func():
        start_time = time.clock()
        func()
        end_time = time.clock()
        # 结束时间
        print("it takes {} s to find all the odds".format(end_time - start_time))
    return improve_func # 返回函数对象

if __name__ == '__main__':
    # 调用 count_time_wrapper 增强函数
    print_odds = count_time_wrapper(print_odds)
    print_odds()

2. 语法糖(Syntactic sugar)

image

3. 装饰器(decorator)

无形参,无返回值

import time

def count_time_wrapper(func):
    # 闭包
    # 用于增强函数 func:给函数 func 增加统计时间的功能
    def improve_func():
        start_time = time.clock()
        func()
        end_time = time.clock()
        # 结束时间
        print("it takes {} s to find all the odds".format(end_time - start_time))
    return improve_func

@count_time_wrapper
def print_odds():
    # 输出0~100之间所有奇数,并统计函数执行时间
    # 查找并输出所有奇数
    for i in range(100):
        if i % 2 == 1:
            print(i)

if __name__ == '__main__':
    # 装饰器等价于在第一次调用函数时执行以下语句:
    # print_odds = count_time_wrapper(print_odds)
    print_odds()

image
image

解决带参数和返回值场景

import time

def count_time_wrapper(func):
    # 闭包
    # 用于增强函数 func:给函数 func 增加统计时间的功能
    def improve_func(*args, **kwargs): # 增强函数应该把接收到的所有参数传给原函数
        start_time = time.clock()
        ret = func(*args, **kwargs) # 执行函数
        end_time = time.clock()
        # 结束时间
        print("it takes {} s to find all the odds".format(end_time - start_time))
        return ret # 增强函数的返回值应该是原函数的返回值

    return improve_func

@count_time_wrapper
def print_odds():
    # 输出0~100之间所有奇数,并统计函数执行时间
    # 查找并输出所有奇数
    for i in range(100):
        if i % 2 == 1:
            print(i)

# @count_time_wrapper
def count_odds(lim = 100):
    cnt = 0
    for i in range(lim):
        if  i % 2 == 1:
            cnt+=1
    return cnt


if __name__ == '__main__':
    print(count_odds(lim=100000)) # 装饰前函数能正常返回,能接受参数
    print('-------------')
    count_odds = count_time_wrapper(count_odds)
    print('-------------')
    print(count_odds(lim=100000)) # 装饰后函数能正常返回,能接受参数

image

多个装饰器的执行顺序

4. *args 和 **kwargs

*args 和 **kwargs 是 Python 中用于处理可变数量参数的语法。

  1. *args
  • *args 允许你传递可变数量的位置参数给一个函数。
  • 当函数被调用时,所有额外的位置参数会被收集到一个元组中。
def example(*args):
    for arg in args:
        print(arg)

example(1, 2, 3)
# 输出:
# 1
# 2
# 3
  1. **kwargs
    **kwargs 允许你传递可变数量的关键字参数给一个函数。
    这些额外的关键字参数会被收集到一个字典中。
def example(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

example(name="Alice", age=30)
# 输出:
# name: Alice
# age: 30

4. 常见面试题

def wraper1(func1):
    print('set func1') # 在 wrapper1 装饰函数时输出

    def improved_func1():
        print('call func1') # 在 wrapper1 装饰过的函数被调用时输出
        func1()

    return improved_func1


def wraper2(func2):
    print('set func2')  # 在 wrapper2 装饰函数时输出

    def improved_func2():
        print('call func2')  # 在 wrapper2 装饰过的函数被调用时输出
        func2()

    return improved_func2

@wraper1
@wraper2
def original_func():
    pass

if __name__ == '__main__':
    original_func()
    print('------')
    original_func()

image

posted @ 2025-09-06 11:31  爱新觉罗LQ  阅读(6)  评论(0)    收藏  举报