装饰器是 Python 中非常强大和灵活的功能。它可以在不改变函数定义的情况下,动态地为函数或方法添加新的功能。本文将带你深入理解装饰器的原理、用法以及一些高级技巧。
一、什么是装饰器?
装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个增强后的函数。装饰器通常用来修改函数的行为,比如增加日志记录、访问控制或性能计时等。
装饰器的基本语法:
@decorator_function
def original_function():
pass
等价于:
def original_function():
pass
original_function = decorator_function(original_function)
二、简单的装饰器示例
示例 1:打印日志
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling function '{func.__name__}' with arguments {args} and {kwargs}")
result = func(*args, **kwargs)
print(f"Function '{func.__name__}' returned {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(3, 5)
输出:
Calling function 'add' with arguments (3, 5) and {}
Function 'add' returned 8
三、装饰器的原理
1. 函数即对象
在 Python 中,函数是一等公民,可以作为参数传递,也可以作为返回值。
2. 闭包
装饰器通常依赖闭包来保存上下文信息。闭包是指在嵌套函数中,内部函数引用了外部函数的变量。
示例:
def outer_function(message):
def inner_function():
print(message)
return inner_function
closure = outer_function("Hello, World!")
closure()
输出:
Hello, World!
四、装饰器的高级用法
1. 为装饰器传递参数
如果需要给装饰器传递参数,可以使用多层嵌套。
示例:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
2. 保留原函数的元数据
使用 functools.wraps
可以保留被装饰函数的原始信息(如名称和文档字符串)。
示例:
from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@logger
def add(a, b):
"""Add two numbers."""
return a + b
print(add.__name__)
print(add.__doc__)
输出:
add
Add two numbers.
3. 装饰类方法
装饰器也可以用于类方法。
示例:
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
class MyClass:
@logger
def greet(self):
print("Hello from MyClass!")
obj = MyClass()
obj.greet()
输出:
Calling greet
Hello from MyClass!
五、内置装饰器
Python 提供了一些常用的内置装饰器:
1. @staticmethod
定义类的静态方法,与类实例无关。
class MyClass:
@staticmethod
def static_method():
print("This is a static method.")
MyClass.static_method()
2. @classmethod
定义类方法,可以访问类本身。
class MyClass:
@classmethod
def class_method(cls):
print(f"This is a class method from {cls}.")
MyClass.class_method()
3. @property
将方法变为属性。
class MyClass:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
obj = MyClass("Alice")
print()
六、多个装饰器的执行顺序
当一个函数有多个装饰器时,它们的执行顺序是自下而上。
示例:
def decorator1(func):
def wrapper(*args, **kwargs):
print("Decorator 1")
return func(*args, **kwargs)
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print("Decorator 2")
return func(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def greet():
print("Hello!")
greet()
输出:
Decorator 1
Decorator 2
Hello!
七、常见的装饰器应用场景
1. 权限验证
def requires_permission(permission):
def decorator(func):
def wrapper(user, *args, **kwargs):
if user.has_permission(permission):
return func(user, *args, **kwargs)
else:
print("Permission denied!")
return wrapper
return decorator
@requires_permission("admin")
def delete_user(user, username):
print(f"User {username} deleted.")
2. 性能分析
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} executed in {end - start:.2f} seconds")
return result
return wrapper
@timer
def compute():
time.sleep(2)
print("Computation done!")
compute()