📢 转载信息
原文链接:https://machinelearningmastery.com/7-python-decorator-tricks-to-write-cleaner-code/
原文作者:Jason Brownlee
Python装饰器七大技巧:写出更简洁优雅的代码
Python 装饰器(Decorators)是语言中一个非常强大且令人兴奋的特性,它允许你在不修改函数源代码的情况下,向函数添加新的功能。本文将介绍七个实用的 Python 装饰器技巧,帮助你写出更简洁、更具表现力的代码。
本文的所有示例代码均假定你已经导入了 `functools` 模块,该模块在编写包装函数时非常有用。
from functools |
functools.wraps
1. 装饰一个函数
这是最简单的装饰器形式。它接受一个函数作为参数,返回一个包装后的函数。
考虑一个需要计算函数执行时间的场景。我们可以创建一个装饰器来完成这个任务。
import time
def timer(func):
def wrapper(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
t1 = time.time()
print(f'Function {func.__name__} took {t1 - t0:.4f} seconds')
return result
return wrapper
@timer
def long_running_function(x):
time.sleep(x)
long_running_function(2)
# Output: Function long_running_function took 2.0004 seconds
这种模式非常常见,但它有一个小问题:它会丢失原始函数的元数据(如 `__name__` 和 `__doc__`)。为了解决这个问题,我们使用 `functools.wraps`。
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
t1 = time.time()
print(f'Function {func.__name__} took {t1 - t0:.4f} seconds')
return result
return wrapper
2. 装饰带有参数的函数
有时我们需要装饰器接受参数,例如,在日志中包含一个自定义消息。
要使装饰器接受参数,我们需要在装饰器之上再添加一个包装层,这个包装层负责接收参数并返回实际的装饰器(即上一个例子中的 `timer` 函数)。
from functools import wraps
def log_execution(message):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f'Starting {func.__name__}: {message}')
result = func(*args, **kwargs)
print(f'Finished {func.__name__}')
return result
return wrapper
return decorator
@log_execution(message='Processing data')
def process_data(data):
return [item * 2 for item in data]
print(process_data([1, 2, 3]))
# Output:
# Starting process_data: Processing data
# Finished process_data
# [2, 4, 6]
3. 装饰返回值的函数
我们可以修改函数的返回值。例如,确保所有返回的字符串都转换为大写。
from functools import wraps
def make_uppercase(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if isinstance(result, str):
return result.upper()
return result
return wrapper
@make_uppercase
def greet(name):
return f'hello {name}'
print(greet('world'))
# Output: HELLO WORLD
4. 装饰类的方法(Class Methods)
装饰类的方法与装饰普通函数非常相似,但需要注意处理 `self` 参数。
from functools import wraps
def log_method_call(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
print(f'Calling method: {func.__name__} on instance {self}')
result = func(self, *args, **kwargs)
return result
return wrapper
class MyClass:
def __init__(self, value):
self.value = value
@log_method_call
def update_value(self, new_value):
self.value = new_value
return self.value
instance = MyClass(10)
instance.update_value(20)
# Output: Calling method: update_value on instance <__main__.MyClass object at 0x...>
5. 使用类作为装饰器
装饰器也可以用类来实现。这在需要维护状态或实现更复杂的逻辑时非常有用。类装饰器必须实现 `__call__` 方法来包装被装饰的函数,并可选地实现 `__init__` 来接收参数。
from functools import wraps
class TimerClass:
def __init__(self, func):
self.func = func
wraps(func)(self)
def __call__(self, *args, **kwargs):
t0 = time.time()
result = self.func(*args, **kwargs)
t1 = time.time()
print(f'Class Decorator: {self.func.__name__} took {t1 - t0:.4f} seconds')
return result
@TimerClass
def heavy_computation(n):
sum(range(n))
heavy_computation(1000000)
# Output: Class Decorator: heavy_computation took 0.0225 seconds
6. 装饰器链(Chaining Decorators)
Python 允许你将多个装饰器应用于同一个函数。装饰器的应用顺序是从下到上(或从内到外)。
# 假设我们有前面定义的 @make_uppercase 和 @timer 装饰器
@timer
@make_uppercase
def format_and_run(text):
print(f"Original: {text}")
return text.lower()
result = format_and_run("HELLO WORLD")
print(f"Final Result: {result}")
# 执行顺序:
# 1. format_and_run('HELLO WORLD') 被 @make_uppercase 装饰
# 2. 结果被 @timer 装饰
# Output:
# Original: HELLO WORLD
# Class Decorator: format_and_run took 0.0000 seconds
# Final Result: hello world
注意:输出可能与你预期的不同,因为装饰器的执行顺序会影响打印内容。在这种情况下,`@timer` 包裹了 `@make_uppercase` 的结果。
7. 使用类装饰器和参数
结合第 2 节和第 5 节的技巧,我们可以创建一个带参数的类装饰器。这需要一个外部函数来接收参数,并返回一个类实例。
from functools import wraps
class LimitCalls:
def __init__(self, max_calls):
self.max_calls = max_calls
self.call_count = 0
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
if self.call_count < self.max_calls:
self.call_count += 1
print(f'Call {self.call_count} of {self.max_calls}')
return func(*args, **kwargs)
else:
print(f'Function {func.__name__} has been called too many times.')
return None
return wrapper
@LimitCalls(max_calls=2)
def limited_function(x):
print(f'Running with {x}')
return x
limited_function(1)
limited_function(2)
limited_function(3)
# Output:
# Call 1 of 2
# Running with 1
# Call 2 of 2
# Running with 2
# Function limited_function has been called too many times.
掌握这些装饰器技巧可以显著提高 Python 代码的模块化、可读性和复用性。
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,小白也可以简单操作。
青云聚合API官网https://api.qingyuntop.top
支持全球最新300+模型:https://api.qingyuntop.top/pricing
详细的调用教程及文档:https://api.qingyuntop.top/about
评论区