Python装饰器详解,提高代码复用性和扩展性
在Python中,装饰器是一种非常强大的语言特性,它可以使用现有函数来修改或增强它们的行为。使用装饰器,可以将某个函数的功能扩展到其他函数中,从而实现代码复用性和扩展性。
1. 装饰器是什么?
装饰器本质上是一个函数,它接受一个函数作为参数并返回一个函数。它可以修改或增强函数的行为,而无需修改函数的原始代码。装饰器通常被用来实现一些横切关注点(cross-cutting concerns),例如日志记录、性能监控、事务管理等。
下面是一个简单的装饰器示例:
```python
def log_wrapper(func):
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_wrapper
def my_function():
print("Hello World!")
my_function()
```
在上面的示例中,log_wrapper是一个装饰器函数,它接受一个参数func,并返回一个函数wrapper。wrapper函数可以在调用原始函数之前或之后执行一些操作。在这个例子中,wrapper函数记录了原始函数的名称,并在调用my_function之前打印它。通过使用@log_wrapper语法,我们将my_function传递给log_wrapper函数,从而创建一个新函数,该函数在调用my_function之前打印日志。
2. 带参数的装饰器
装饰器不仅可以接受函数作为参数,还可以接受其他参数。这使得我们可以创建带有配置选项的装饰器。
例如,我们可以编写一个带有阈值参数的装饰器,该参数指定函数执行的最长时间。如果函数的执行时间超过了阈值,则装饰器将打印一条警告消息:
```python
import time
def time_limit(threshold):
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
duration = end_time - start_time
if duration > threshold:
print(f"Warning: function {func.__name__} took too long ({duration} seconds)")
return result
return wrapper
return decorator
@time_limit(0.1)
def my_slow_function():
time.sleep(0.2)
print("Done")
my_slow_function()
```
在上面的示例中,我们定义了一个名为time_limit的装饰器函数,它接受一个阈值参数,并返回一个装饰器函数decorator。decorator函数接受一个func参数,并返回一个包裹函数wrapper,该函数在调用原始函数之前记录开始时间,在调用之后记录结束时间,并计算两者之差。如果持续时间超过了阈值,则打印一条警告消息。
我们使用@time_limit(0.1)语法将my_slow_function传递给time_limit装饰器,从而创建一个新函数,该函数在调用my_slow_function之前检查其执行时间是否超过0.1秒。
3. 多个装饰器
一个函数可以有多个装饰器。这意味着多个装饰器可以依次对同一个函数进行修饰。
例如,我们可以编写一个带有缓存和时间限制功能的函数:
```python
import time
def cache(func):
cache_dict = {}
def wrapper(*args):
if args in cache_dict:
print(f"Result cached for {func.__name__} {args}")
return cache_dict[args]
else:
result = func(*args)
cache_dict[args] = result
return result
return wrapper
def time_limit(threshold):
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
duration = end_time - start_time
if duration > threshold:
print(f"Warning: function {func.__name__} took too long ({duration} seconds)")
return result
return wrapper
return decorator
@cache
@time_limit(0.1)
def my_slow_function(arg):
time.sleep(0.2)
return arg
my_slow_function(1)
my_slow_function(1)
```
在上面的示例中,我们定义了两个装饰器函数cache和time_limit,并使用@cache和@time_limit语法将它们应用到my_slow_function中。由于@cache装饰器先被调用,因此在调用my_slow_function时,它首先检查缓存中是否存在结果。如果结果已经缓存,它将立即返回结果,而无需再次执行函数。否则,它将执行函数并将结果缓存。
@time_limit装饰器在这之后被调用。它使用相同的方式对my_slow_function进行修饰,但它添加了一个时间限制,以便在函数执行超过0.1秒时打印一条警告消息。
4. 类装饰器
装饰器不仅可以是函数,还可以是类。类装饰器可以用来修改类的定义或行为。
例如,我们可以编写一个类装饰器,该装饰器将类中的所有函数都替换为打印日志的版本:
```python
class LogWrapper:
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
for name, value in vars(self.cls).items():
if callable(value):
setattr(self.cls, name, self.log_func(value))
return self.cls(*args, **kwargs)
def log_func(self, func):
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__}")
return func(*args, **kwargs)
return wrapper
@LogWrapper
class MyClass:
def foo(self):
print("Hello World!")
def bar(self):
print("Goodbye World!")
my_object = MyClass()
my_object.foo()
my_object.bar()
```
在上面的示例中,我们定义了一个名为LogWrapper的类装饰器,它接受一个类作为参数,并将该类中的所有函数替换为打印日志版本。LogWrapper类的__call__方法遍历原始类中的所有属性,并检查是否可调用。如果是,则将其替换为一个新函数,该函数在调用原始函数之前打印日志。
我们使用@LogWrapper语法将MyClass传递给LogWrapper装饰器,从而创建一个新类,其中所有函数都被修改为包含日志记录的版本。当我们创建一个MyClass对象并调用其中的方法时,它们都将打印日志。
5. 结论
Python装饰器是一种非常有用的语言特性,它允许我们编写可重用的代码,提高代码复用性和扩展性。使用装饰器,我们可以轻松地在不修改原始代码的情况下修改或增强函数的行为。我们可以使用装饰器来实现日志记录、性能监控、事务管理等横切关注点,也可以使用装饰器创建带有配置选项的函数。在Python中,装饰器是一个非常强大的工具,值得深入学习和使用。