好的,各位观众老爷们,欢迎来到“Python 装饰器进阶:参数化、类装饰器与装饰器工厂”特别节目!今天咱们要一起解锁装饰器的高级玩法,让你的代码瞬间逼格满满,成为同事眼中的大神!
第一幕:参数化装饰器,让装饰器更灵活!
话说,装饰器这玩意儿,用起来是方便,但有时候我们想让它更个性化一点,比如根据不同的情况执行不同的操作。这时候,参数化装饰器就派上用场了。
啥是参数化装饰器?
简单来说,就是在装饰器外面再套一层函数,这层函数负责接收参数,然后返回一个真正的装饰器。
代码示例:带参数的日志记录装饰器
假设我们想写一个日志记录装饰器,可以指定日志级别(比如debug, info, warning)。
import functools
import logging
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_level(level):
"""
参数化装饰器,用于指定日志级别
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.log(level, f"Calling function: {func.__name__}")
result = func(*args, **kwargs)
logging.log(level, f"Function {func.__name__} returned: {result}")
return result
return wrapper
return decorator
@log_level(logging.DEBUG) # 使用DEBUG级别
def add(x, y):
"""
一个简单的加法函数
"""
return x + y
@log_level(logging.WARNING) # 使用WARNING级别
def divide(x, y):
"""
一个简单的除法函数
"""
if y == 0:
return "除数不能为0"
return x / y
# 测试
print(add(5, 3))
print(divide(10, 2))
print(divide(10, 0))
代码解读:
log_level(level)
: 这就是我们的参数化装饰器,它接收一个level
参数,表示日志级别。decorator(func)
:log_level
函数返回一个真正的装饰器decorator
,它接收一个函数func
作为参数。- *`wrapper(args, kwargs)`:
decorator
函数返回一个包装器wrapper
,它负责在函数执行前后记录日志。 @log_level(logging.DEBUG)
: 使用@log_level(logging.DEBUG)
相当于add = log_level(logging.DEBUG)(add)
。 先调用log_level(logging.DEBUG)
拿到装饰器,然后再用这个装饰器去装饰add
函数。
原理剖析:
参数化装饰器其实就是一个函数嵌套。外层函数接收参数,内层函数才是真正的装饰器。 这种方式赋予了装饰器极大的灵活性,我们可以根据不同的参数来定制装饰器的行为。
第二幕:类装饰器,让装饰器更有状态!
除了函数,类也能当装饰器用!类装饰器最大的优势是可以保存状态,这在某些场景下非常有用。
啥是类装饰器?
简单来说,就是把一个类当成装饰器来使用。 实现方式是让类实现 __call__
方法,这样类的实例就可以像函数一样被调用。
代码示例:统计函数调用次数的类装饰器
import functools
class CallCounter:
"""
类装饰器,用于统计函数调用次数
"""
def __init__(self, func):
self.func = func
self.count = 0
functools.wraps(func)(self) # 保留原函数的信息
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Function {self.func.__name__} called {self.count} times")
return self.func(*args, **kwargs)
@CallCounter
def multiply(x, y):
"""
一个简单的乘法函数
"""
return x * y
# 测试
print(multiply(2, 3))
print(multiply(4, 5))
print(multiply(6, 7))
代码解读:
CallCounter(func)
: 类的构造函数接收一个函数func
作为参数,并初始化计数器count
为 0。- *`call(self, args, kwargs)`:
__call__
方法让类的实例可以像函数一样被调用。 每次调用时,计数器count
加 1,并打印调用次数。 @CallCounter
: 使用@CallCounter
相当于multiply = CallCounter(multiply)
。 创建CallCounter
的实例,并将multiply
函数作为参数传递给构造函数。
原理剖析:
当使用 @CallCounter
装饰 multiply
函数时,实际上创建了一个 CallCounter
类的实例,并将 multiply
函数作为参数传递给该实例。 当调用 multiply(2, 3)
时,实际上是调用了 CallCounter
实例的 __call__
方法,从而实现了对函数调用的拦截和统计。
类装饰器的优势:
- 状态保持: 类可以保存状态,比如计数器、缓存等。
- 代码组织: 可以将装饰器的逻辑封装在类中,使代码更清晰。
带参数的类装饰器
import functools
class CallCounterWithLimit:
"""
带参数的类装饰器,用于统计函数调用次数,并限制最大调用次数
"""
def __init__(self, max_calls):
self.max_calls = max_calls
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if wrapper.count < self.max_calls:
wrapper.count += 1
print(f"Function {func.__name__} called {wrapper.count} times (limit: {self.max_calls})")
return func(*args, **kwargs)
else:
print(f"Function {func.__name__} call limit reached ({self.max_calls})")
return None # 或者抛出异常
wrapper.count = 0 # 初始化调用次数
return wrapper
@CallCounterWithLimit(max_calls=3)
def greet(name):
"""
一个简单的问候函数
"""
return f"Hello, {name}!"
# 测试
print(greet("Alice"))
print(greet("Bob"))
print(greet("Charlie"))
print(greet("David")) # 超过限制
print(greet("Eve")) # 超过限制
代码解读:
CallCounterWithLimit(max_calls)
: 构造函数接收max_calls
参数,用于限制最大调用次数。__call__(self, func)
:__call__
方法接收被装饰的函数func
,并返回一个wrapper
函数。wrapper.count = 0
: 注意这里, 我们在wrapper函数上定义了一个count属性,用来记录被装饰函数的调用次数。
第三幕:装饰器工厂,批量生产装饰器!
有时候,我们需要创建多个相似的装饰器,这时候就可以使用装饰器工厂来简化代码。
啥是装饰器工厂?
装饰器工厂就是一个返回装饰器的函数。 它接收一些配置参数,然后根据这些参数生成不同的装饰器。
代码示例:创建重试装饰器的工厂
import time
import functools
def retry(max_attempts, delay=1):
"""
装饰器工厂,用于创建重试装饰器
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
print(f"Attempt {attempts} failed: {e}")
time.sleep(delay)
print(f"Function {func.__name__} failed after {max_attempts} attempts")
return None # 或者抛出异常
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def unreliable_function():
"""
一个可能失败的函数
"""
import random
if random.random() < 0.5:
raise Exception("Function failed!")
return "Function succeeded!"
# 测试
print(unreliable_function())
代码解读:
retry(max_attempts, delay=1)
: 这就是我们的装饰器工厂,它接收max_attempts
(最大重试次数)和delay
(重试间隔)两个参数。decorator(func)
:retry
函数返回一个真正的装饰器decorator
,它接收一个函数func
作为参数。- *`wrapper(args, kwargs)`:
decorator
函数返回一个包装器wrapper
,它负责在函数执行失败时进行重试。 @retry(max_attempts=3, delay=0.5)
: 使用@retry(max_attempts=3, delay=0.5)
相当于unreliable_function = retry(max_attempts=3, delay=0.5)(unreliable_function)
。
原理剖析:
装饰器工厂的本质还是函数嵌套。 外层函数接收配置参数,内层函数才是真正的装饰器。 装饰器工厂可以根据不同的配置参数生成不同的装饰器,从而避免了重复编写相似的装饰器代码。
总结:
我们今天学习了装饰器的高级用法,包括:
- 参数化装饰器: 让装饰器可以接收参数,更加灵活。
- 类装饰器: 让装饰器可以保存状态,适用于某些特定场景。
- 装饰器工厂: 批量生产装饰器,简化代码。
希望今天的课程能帮助你更深入地理解装饰器,并在实际开发中灵活运用。 记住,编程的乐趣在于不断学习和探索! 感谢大家的观看,我们下期再见!
表格总结:
特性 | 参数化装饰器 | 类装饰器 | 装饰器工厂 |
---|---|---|---|
核心概念 | 装饰器外层嵌套函数,用于接收参数 | 使用类实现装饰器,通过__call__ 方法实现调用 |
返回装饰器的函数,用于批量生成装饰器 |
优势 | 灵活,可根据参数定制装饰器行为 | 可保存状态,代码组织清晰 | 简化代码,避免重复编写相似装饰器 |
实现方式 | 多层函数嵌套 | 实现__call__ 方法的类 |
返回装饰器的函数 |
适用场景 | 需要根据不同情况执行不同操作的装饰器 | 需要保存状态的装饰器,如计数器、缓存等 | 需要创建多个相似装饰器的场景 |
示例 | 日志级别可配置的日志记录装饰器 | 统计函数调用次数的装饰器 | 创建重试装饰器的工厂 |
补充说明:
functools.wraps
: 这个装饰器很重要! 它可以保留被装饰函数的元信息,比如函数名、文档字符串等。 强烈建议在编写装饰器时使用它,以保证代码的可读性和可维护性。- 异常处理: 在编写装饰器时,要考虑异常处理。 尽量不要让装饰器影响被装饰函数的正常执行。
- 性能: 装饰器会增加函数调用的开销。 在性能敏感的场景下,要谨慎使用装饰器。
希望这些补充说明能让你对装饰器有更全面的了解! 记住,实践是检验真理的唯一标准! 多写代码,多思考,你也能成为装饰器大师!