装饰器的本质

装饰器本质上就是高阶函数的"语法糖"。你想啊,装饰器做的事情就是:接收一个函数,包一层,返回一个新的函数。这不就是典型的高阶函数吗?

@decorator 等价于 func = decorator(func),就是把原函数传给装饰器,装饰器决定什么时候调用它、传什么参数进去,然后再返回一个"升级版"的函数。

装饰器的基本结构

让我们来看一个最简单的装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def simple_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper

@simple_decorator
def say_hello(name):
print(f"Hello, {name}!")

# 调用函数
say_hello("Alice")

输出:

1
2
3
Before function call
Hello, Alice!
After function call

装饰器的工作原理

  1. 定义装饰器simple_decorator 是一个高阶函数,它接收一个函数 func 作为参数。
  2. 定义内部函数wrapper 函数是一个闭包,它可以访问外部函数的变量 func
  3. 返回内部函数simple_decorator 返回 wrapper 函数。
  4. 应用装饰器@simple_decorator 语法将 say_hello 函数传递给 simple_decorator,然后将返回的 wrapper 函数赋值给 say_hello
  5. 调用函数:当我们调用 say_hello("Alice") 时,实际上是在调用 wrapper("Alice")
  6. 执行包装逻辑wrapper 函数先执行一些前置逻辑,然后调用原始的 func 函数,最后执行一些后置逻辑。

装饰器的参数传递

装饰器可以接收参数,这使得装饰器更加灵活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def decorator_with_args(prefix):
def decorator(func):
def wrapper(*args, **kwargs):
print(f"{prefix}: Before function call")
result = func(*args, **kwargs)
print(f"{prefix}: After function call")
return result
return wrapper
return decorator

@decorator_with_args("DEBUG")
def say_hello(name):
print(f"Hello, {name}!")

# 调用函数
say_hello("Alice")

输出:

1
2
3
DEBUG: Before function call
Hello, Alice!
DEBUG: After function call

装饰器的应用场景

1. 日志记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def log_decorator(func):
def wrapper(*args, **kwargs):
import datetime
print(f"[{datetime.datetime.now()}] Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"[{datetime.datetime.now()}] {func.__name__} returned {result}")
return result
return wrapper

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

# 调用函数
result = add(3, 5)
print(f"Result: {result}")

2. 性能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def timer_decorator(func):
def wrapper(*args, **kwargs):
import time
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds")
return result
return wrapper

@timer_decorator
def slow_function():
import time
time.sleep(1)
return "Done"

# 调用函数
result = slow_function()
print(f"Result: {result}")

3. 权限验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def require_admin(func):
def wrapper(*args, **kwargs):
# 假设当前用户信息存储在全局变量中
current_user = {"name": "Alice", "is_admin": True}
if not current_user.get("is_admin", False):
raise PermissionError("Admin permission required")
return func(*args, **kwargs)
return wrapper

@require_admin
def delete_user(user_id):
print(f"Deleting user {user_id}")
return True

# 调用函数
try:
result = delete_user(123)
print(f"Result: {result}")
except PermissionError as e:
print(f"Error: {e}")

装饰器的注意事项

1. 函数名称和文档字符串

当我们使用装饰器时,原始函数的名称和文档字符串会被包装函数覆盖。为了解决这个问题,我们可以使用 functools.wraps 装饰器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import functools

def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper function"""
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper

@decorator
def say_hello(name):
"""Say hello to someone"""
print(f"Hello, {name}!")

# 查看函数名称和文档字符串
print(f"Function name: {say_hello.__name__}")
print(f"Function doc: {say_hello.__doc__}")

2. 装饰器的顺序

当多个装饰器应用于同一个函数时,它们的执行顺序是从下到上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def decorator1(func):
def wrapper(*args, **kwargs):
print("Decorator 1: Before")
result = func(*args, **kwargs)
print("Decorator 1: After")
return result
return wrapper

def decorator2(func):
def wrapper(*args, **kwargs):
print("Decorator 2: Before")
result = func(*args, **kwargs)
print("Decorator 2: After")
return result
return wrapper

@decorator1
@decorator2
def say_hello(name):
print(f"Hello, {name}!")

# 调用函数
say_hello("Alice")

输出:

1
2
3
4
5
Decorator 1: Before
Decorator 2: Before
Hello, Alice!
Decorator 2: After
Decorator 1: After

总结

装饰器是Python中一种非常强大的特性,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。装饰器本质上是高阶函数的语法糖,它接收一个函数,返回一个新的函数。

通过本文的介绍,你应该已经对装饰器的本质和工作原理有了更深入的理解。现在,你可以尝试在自己的代码中使用装饰器,写出更加简洁、优雅的Python代码。