Python-装饰器

Python 装饰器(Decorator)是一种允许在不修改函数或方法代码的情况下,动态地为其添加额外功能的设计模式。装饰器常用于对函数的输入或输出进行处理、增加日志记录、性能监控、访问控制等。理解装饰器的关键在于理解 Python 函数是“第一类对象”(First-class objects),即函数可以作为参数传递,也可以作为返回值返回。

1. 基本原理

装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个新的函数(可以是原函数或修改后的函数)。这个新函数通常包含了附加的功能逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
# 在原函数执行前可以添加一些操作
print("Before executing the function.")

result = original_function(*args, **kwargs)

# 在原函数执行后可以添加一些操作
print("After executing the function.")

return result

return wrapper_function

2. 基本装饰器的实现和使用

我们可以通过 @decorator_name 的方式来使用装饰器,它会将 decorator_function 应用到目标函数上。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 定义装饰器
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper

# 使用装饰器
@my_decorator
def say_hello():
print("Hello!")

# 调用
say_hello()
1
2
3
4
5
输出

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

在这个例子中,my_decorator 是装饰器函数,它接收 say_hello 作为参数并返回 wrapper。当 say_hello 被调用时,实际调用的是 wrapper 函数,这样可以在原函数的前后增加功能。

3. 带参数的装饰器

装饰器可以接收参数。要实现这一点,我们需要再嵌套一层函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat

@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")

greet("Alice")
1
2
3
4
5
输出

Hello Alice
Hello Alice
Hello Alice

在这里,repeat 是一个带参数的装饰器。repeat(num_times) 返回 decorator_repeat,然后 decorator_repeat 装饰 greet 函数。每次调用 greet(“Alice”),它会根据 num_times 重复执行。

4. 装饰器的应用场景

日志记录:记录函数的执行时间、参数和返回值等。

性能测试:计算函数执行时间,判断性能。

访问控制:验证用户权限。

缓存:将函数结果缓存起来,避免重复计算。

5. functools.wraps 保留原函数元数据

使用装饰器后,原函数的元数据(如函数名和文档字符串)会被覆盖。为了解决这个问题,可以使用 functools.wraps 来保持原函数的信息。

1
2
3
4
5
6
7
8
import functools

def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Calling decorated function")
return func(*args, **kwargs)
return wrapper

这样,装饰器 wrapper 会保留被装饰函数的元数据。

6. 类方法的装饰器

装饰器也可以用于类的方法,特别是在需要对实例方法或类方法应用通用功能(如日志记录或权限检查)时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def method_decorator(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
print(f"Calling method {method.__name__} with args {args} and kwargs {kwargs}")
return method(self, *args, **kwargs)
return wrapper

class MyClass:
@method_decorator
def display(self, value):
print(f"Display: {value}")

# 使用
obj = MyClass()
obj.display("Test")

7. 总结

装饰器是非常强大的工具,可以在函数前后添加额外逻辑。使用装饰器可以有效提升代码的可复用性和可维护性。掌握装饰器可以帮助开发者在实际项目中更加优雅地管理代码。