Python 装饰器高级用法
在Python函数式编程中,装饰器是一种强大而优雅的工具,它允许我们在不修改函数代码的情况下,增强或修改函数的行为。如果你已经掌握了基本的装饰器概念,那么是时候探索一些更高级的用法了。
回顾装饰器基础
装饰器本质上是一个接受函数作为参数并返回一个新函数的函数。基本形式如下:
def decorator(func):
def wrapper(*args, **kwargs):
# 在调用原始函数前执行的代码
result = func(*args, **kwargs)
# 在调用原始函数后执行的代码
return result
return wrapper
# 使用装饰器
@decorator
def my_function():
pass
备注
上面的@decorator
语法是Python的语法糖,等同于my_function = decorator(my_function)
。
高级装饰器技术
1. 带参数的装饰器
当我们需要自定义装饰器的行为时,可以创建带参数的装饰器。这实际上是一个返回装饰器的函数。
def repeat(n=1):
def decorator(func):
def wrapper(*args, **kwargs):
result = None
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
# 使用带参数的装饰器
@repeat(3)
def say_hello(name):
print(f"Hello, {name}!")
return name
# 测试
say_hello("Alice")
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
这个例子中,repeat(3)
返回一个装饰器,该装饰器使函数say_hello
重复执行3次。
2. 保留原函数元数据
当使用装饰器时,被装饰函数的一些元数据(如函数名、文档字符串等)会丢失。可以使用functools.wraps
来保留这些信息:
import functools
def my_decorator(func):
@functools.wraps(func) # 保留原函数的元数据
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def say_hello(name):
"""这是一个打招呼的函数"""
print(f"Hello, {name}!")
# 查看函数的名称和文档
print(say_hello.__name__) # 输出: say_hello(而不是wrapper)
print(say_hello.__doc__) # 输出: 这是一个打招呼的函数
3. 类装饰器
装饰器不仅可以装饰函数,还可以装饰类。类装饰器通常用 于修改类的属性或行为。
def add_greeting(cls):
cls.greet = lambda self: f"Hello, I'm {self.name}"
return cls
@add_greeting
class Person:
def __init__(self, name):
self.name = name
# 测试
person = Person("Alice")
print(person.greet()) # 输出: Hello, I'm Alice
4. 类作为装饰器
类本身也可以作为装饰器使用,只需要实现__call__
方法:
class CountCalls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"Function {self.func.__name__} has been called {self.calls} times")
return self.func(*args, **kwargs)
@CountCalls
def say_hello(name):
print(f"Hello, {name}!")
# 测试
say_hello("Alice") # 输出: Function say_hello has been called 1 times, Hello, Alice!
say_hello("Bob") # 输出: Function say_hello has been called 2 times, Hello, Bob!
5. 装饰器链
可以将多个装饰器应用于同一个函数,形成装饰器链。执行顺序是从最靠近函数的装饰器开始,向外执行。
def bold(func):
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper
def italic(func):
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper
@bold
@italic
def format_text(text):
return text
print(format_text("Hello")) # 输出: <b><i>Hello</i></b>
提示
装饰器的执行顺序是从下到上的。在上面的例子中,先执行italic
装饰器,然后执行bold
装饰器。