Files
Cloud-book/Python/Python面向函数/装饰器函数.md
2025-08-27 17:10:05 +08:00

350 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Python 装饰器
在 Python 中,装饰器是一个非常强大的功能,可以在不修改函数代码的情况下,动态地修改函数/方法的行为。装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。
应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
## 案例切入
已知有一个函数 `func1()`,作用是输出一句话。现在我们想要给他增加额外的功能。但是为了保障已有功能的稳定,不能更改原函数。
```python
def func1():
print("in func1")
# 新的需求,能够打印如下内容...
# hello world
# in func1
# hello python
# 实现
def func2(func):
def inner():
print("hello world")
func()
print("hello python")
return inner
func1 = func2(func1)
func1()
```
## 装饰器形成的过程
如果我想测试某个函数的执行时间
```python
import time
def func1():
print('in func1')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1) # 将函数本身做为参数传递进去
func1()
```
如果有很多个函数都需要测试它们的执行时间,每次都需要 `func1 = timer(func1)` 是比较麻烦的,而且不利于代码的可读性和后期维护。这里我们可以使用 python 中的一种特殊的语法结构**语法糖**,来更为简便的使用装饰器。
我们将上述代码修改如下:
```python
import time
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@timer
"""
当我们在某个函数上方使用 @my_decorator 的时候python 会自动将下面定义的函数做为参数传递给 my_decorator。
func1 = timer(func1)
"""
def func1():
time.sleep(1)
print('in func1')
func1()
```
## 装饰带参数的函数
装饰一个带参数的函数与装饰一个不带参数的函数类似,但需要在装饰器中处理传递给被装饰函数的参数。
**示例:**
```python
import time
def timer(func):
def inner(a):
start = time.time()
func(a)
print(time.time() - start)
return inner
@timer
"""
func1 = timer(func1)
"""
def func1(a):
time.sleep(1)
print(a)
func1('hello world')
```
## 装饰带多个参数的函数
这里我们利用了函数里面的动态参数进行传参
```python
def my_decorator(func):
def wrapper(*args, **kwargs):
# 打印传入的参数
print(f"调用 {func.__name__} 函数,参数: {args}, 关键字参数: {kwargs}")
# 调用原始函数并获取结果
result = func(*args, **kwargs)
# 打印返回结果
print(f"{func.__name__} 函数返回: {result}")
return result
return wrapper
@my_decorator
def add(x, y):
"""返回两个数的和"""
return x + y
# 测试
result = add(5, 3)
print(f"最终结果: {result}")
```
# wraps装饰器
回到我们最开始的案例
```python
import time
def func1():
print('in func1')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1) # 将函数本身做为参数传递进去
func1()
```
思考一个问题:这里虽然我们最后还是执行 `func1` 函数,但是这里的 `func1` 函数还是我们最初的`func1`函数吗?
......
我们先来看一下最后的 `func1` 他的函数名
```python
import time
def func1():
print('in func1')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1) # 将函数本身做为参数传递进去
print(func1.__name__) # 查看函数的名称
```
## 导入wraps装饰器
`wraps` 装饰器,用于帮助创建装饰器时保留被装饰函数的元数据(如名称、文档字符串等)。使用 `@wraps` 可以确保装饰后的函数看起来像原始函数,这样有助于调试和文档生成。
我们将上方的案例使用 wraps 装饰器装饰
```python
from functools import wraps
import time
def func1():
print('in func1')
def timer(func):
@wraps(func)
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1) # 将函数本身做为参数传递进去
print(func1.__name__) # 查看函数的名称
```
# 带参数的装饰器
带参数的装饰器允许你在装饰器中接受参数,从而增强装饰器的灵活性和功能性。实现带参数的装饰器通常需要使用嵌套函数。
我们将创建一个装饰器,它接受一个参数,用于指定**是否打印函数的执行时间**。
```python
import time
from functools import wraps
def timing_decorator(print_time=True):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time() # 记录开始时间
result = func(*args, **kwargs) # 调用原始函数
end_time = time.time() # 记录结束时间
if print_time:
execution_time = end_time - start_time
print(f"{func.__name__} 执行时间: {execution_time:.4f}秒")
return result
return wrapper
return decorator
@timing_decorator(print_time=True)
"""
add = timing_decorator(print_time=True)(add)
"""
def add(x, y):
"""返回两个数的和"""
time.sleep(1) # 模拟耗时操作
return x + y
@timing_decorator(print_time=False)
def multiply(x, y):
"""返回两个数的积"""
time.sleep(1) # 模拟耗时操作
return x * y
# 测试
result_add = add(5, 3)
print(f"加法结果: {result_add}")
result_multiply = multiply(5, 3)
print(f"乘法结果: {result_multiply}")
```
# 多个装饰器装饰一个函数
可以将多个装饰器应用于同一个函数。这种情况下,装饰器会按照从内到外的顺序依次应用。
```python
def wrapper1(func):
def inner1():
print('第一个装饰器,在程序运行之前')
func()
print('第一个装饰器,在程序运行之后')
return inner1
def wrapper2(func):
def inner2():
print('第二个装饰器,在程序运行之前')
func()
print('第二个装饰器,在程序运行之后')
return inner2
@wrapper1
@wrapper2
def f():
print('Hello')
f()
"""
执行过程分析:
1. f = wrapper2(f) -> func = f, return inner2
2. f = wrapper1(f) -> func = inner2, return inner1
3. f() = inner1() -> inner2() -> f() -> inner2() -> inner1()
"""
```
## 示例:多个装饰器
创建两个装饰器,一个用于打印函数的执行时间,另一个用于打印调用的参数。
```python
import time
from functools import wraps
# 装饰器 1打印执行时间
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"{func.__name__} 执行时间: {execution_time:.4f}秒")
return result
return wrapper
# 装饰器 2打印函数参数
def logging_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__} 函数,参数: {args}, 关键字参数: {kwargs}")
return func(*args, **kwargs)
return wrapper
@timing_decorator
@logging_decorator
def add(x, y):
"""返回两个数的和"""
time.sleep(1) # 模拟耗时操作
return x + y
# 测试
result = add(5, 3)
print(f"加法结果: {result}")
```
# 装饰器的固定结构
```python
from functools import wraps
def decorator_with_args(param):
def actual_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"装饰器参数: {param}")
return func(*args, **kwargs)
return wrapper
return actual_decorator
@decorator_with_args("配置参数")
def my_function():
pass
```