day27 python 装饰器
目录
一、装饰器的基本概念
示例:用装饰器优化质数查找函数
二、装饰器的高级用法
1. 支持任意参数的装饰器
2. 装饰器的返回值处理
在 Python 编程中,装饰器是一个非常强大的功能,它可以让其他函数或方法在不需要做任何代码修改的前提下增加额外功能。装饰器本质上是一个 Python 函数,它遵循“不要重复自己”(DRY)的原则,通过封装可复用的功能,使代码更加简洁、可读性更高。
一、装饰器的基本概念
装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新函数来替代原函数。这个新函数需要保留原函数的调用方式(参数和返回值),同时在原函数执行前后添加额外逻辑(如计时、日志等)。
示例:用装饰器优化质数查找函数
假设我们需要计算 2 到 9999 的所有质数,并打印找到这些数所需的时间。普通的实现方式如下:
import timedef is_prime(num):if num < 2:return Falseelif num == 2:return Trueelse:for i in range(2, num):if num % i == 0:return Falsereturn Truedef 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()
在这个例子中,time
模块的代码与质数查找的逻辑混杂在一起,使得代码可读性较差。如果我们将计时功能通过装饰器实现,代码会更加清晰:
import time# 定义一个装饰器
def display_time(func):def wrapper():start_time = time.time()func() # 调用原函数end_time = time.time()print(f"执行时间: {end_time - start_time} 秒")return wrapper# 使用装饰器
@display_time
def prime_nums():for i in range(2, 10000):if is_prime(i):print(i)prime_nums()
装饰器的底层逻辑是将原函数传递给装饰器函数,然后用装饰器返回的新函数覆盖原函数。例如,@display_time
等价于以下代码:
prime_nums = display_time(prime_nums)
二、装饰器的高级用法
1. 支持任意参数的装饰器
在实际开发中,被装饰的函数可能需要接收参数。因此,装饰器需要支持任意数量的位置参数和关键字参数。通过使用 *args
和 **kwargs
,可以实现这一点:
import timedef display_time(func):"""支持任意参数的时间统计装饰器"""def wrapper(*args, **kwargs):t1 = time.time()result = func(*args, **kwargs) # 将参数传递给原函数t2 = time.time()print(f"函数执行时间: {t2 - t1} 秒")return result # 返回原函数的返回值return wrapper@display_time
def add(a, b):return a + badd(3, 5) # 输出:函数执行时间: 0.0 秒,返回值:8
2. 装饰器的返回值处理
如果被装饰的函数有返回值,装饰器需要正确处理并返回。例如:
def logger(func):def wrapper(*args, **kwargs):print(f"开始执行函数 {func.__name__},参数: {args}, {kwargs}")result = func(*args, **kwargs)print(f"函数 {func.__name__} 执行完毕,返回值: {result}")return resultreturn wrapper@logger
def multiply(a, b):return a * bmultiply(2, 3) # 输出:开始执行函数 multiply,参数: (2, 3), {},返回值:6
multiply(a=2, b=3) # 输出:开始执行函数 multiply,参数: (), {'a': 2, 'b': 3},返回值:6
multiply(2, b=3) # 输出:开始执行函数 multiply,参数: (2,), {'b': 3},返回值:6
需要注意的是,关键字参数必须跟在所有位置参数之后,否则会报错。
@浙大疏锦行