装饰器(Decorators)是一种方便的设计模式,它允许在不修改函数或类的情况下,动态地增加或改变其行为。
本质上是一个函数,它接受另一个函数作为参数并返回一个新函数,通常是对原函数的某种增强或修改。
一、基本语法
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
# 在调用原始函数之前可以添加代码
print("Wrapper executed before {}".format(original_function.__name__))
# 调用原始函数
result = original_function(*args, **kwargs)
# 在调用原始函数之后可以添加代码
print("Wrapper executed after {}".format(original_function.__name__))
return result
return wrapper_function
@decorator_function
def display():
print("Display function executed.")
# 调用被装饰函数
display()
代码解读
1. 定义装饰器:
decorator_function
是装饰器,它接受一个函数作为参数(original_function
)。- 在
decorator_function
内部,定义了一个新的函数wrapper_function
,这个函数将在原始函数之前和之后执行额外的代码。2. 使用装饰器:
- 使用
@decorator_function
语法将display
函数装饰为wrapper_function
。相当于display
作为original_function
传入。- 当调用
display()
时,实际上调用的是wrapper_function
。3. 输出结果:
- 调用
display()
时,输出将是:
二、工作原理
1. 定义装饰器:定义一个装饰器函数。这个函数将接受一个函数作为参数,并且通常会返回一个新的函数。
def my_decorator(func):
# 这里是装饰器
pass
2. 定义包装函数:在装饰器函数内部,定义一个包装函数(wrapper)。这个包装函数会在原始函数执行前后添加额外的功能。
def my_decorator(func):
def wrapper():
# 在调用原始函数之前执行的代码
print("Before the function call")
func() # 调用原始函数
# 在调用原始函数之后执行的代码
print("After the function call")
return wrapper
3. 返回包装函数:装饰器函数返回包装函数。这样,当使用这个装饰器时,原始函数会被包装函数所替代。
def my_decorator(func):
def wrapper():
print("Before the function call")
func()
print("After the function call")
return wrapper
4. 使用@语法:在需要被装饰的函数定义上方使用 @decorator_name
语法。Python 解释器会自动将该函数作为参数传递给装饰器,并将返回的包装函数替代原始函数。
@my_decorator
def say_hello():
print("Hello!")
# 调用装饰后的函数
say_hello()
三、进阶应用
3.1 带参数的装饰器
带参数的装饰器允许您在装饰器外部传递参数,以便可以根据需要配置装饰器的行为。
带参数的装饰器一般是通过定义一个外层函数(用于接受参数),并在其内部返回一个装饰器函数来实现的。
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 调用函数
repeat(num_times)
: 外层函数,接受参数num_times
。decorator_repeat(func)
: 内层装饰器函数,接受一个函数作为参数。wrapper(*args, **kwargs)
: 包装函数,负责调用原始函数多次。
输出结果:
3.2 装饰器带参数
装饰器的 "带参数" 意味着我们可以让装饰器接受不同的参数,通过这些参数来影响装饰器的行为。即装饰器中的包装函数(wrapper)需要接收这些参数,通常通过 *args 和 **kwargs 实现。
举例:带参数的日志记录装饰器的示例,可以灵活地根据需要记录不同级别的日志。
def log(level):
def decorator_log(func):
def wrapper(*args, **kwargs):
print(f"[{level.upper()}] Calling function '{func.__name__}' with arguments: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"[{level.upper()}] Function '{func.__name__}' returned: {result}")
return result
return wrapper
return decorator_log
@log(level='info')
def add(a, b):
return a + b
add(2, 3) # 调用函数
输出结果:
3.3 类装饰器
类装饰器是一个函数,它接受一个类作为参数,并返回一个新的类或修改后的同一个类。类装饰器可以用来添加方法、属性,或者修改类的行为。
3.3.1 添加方法
def my_class_decorator(cls):
# 在类中添加一个新方法
def new_method(self):
return "This is a new method added to the class."
cls.new_method = new_method
return cls
@my_class_decorator
class MyClass:
def original_method(self):
return "This is the original method."
# 创建类的实例
instance = MyClass()
print(instance.original_method()) # 调用原始方法
print(instance.new_method()) # 调用新添加的方法
3.3.2 添加或修改类的属性
def add_class_attribute(cls):
cls.new_attribute = "This is a new class attribute."
return cls
@add_class_attribute
class MyClass:
pass
# 访问新添加的类属性
print(MyClass.new_attribute) # 输出: This is a new class attribute.
3.3.3 修改类的__init__方法
def modify_init(cls):
# 保存原始的 __init__ 方法
original_init = cls.__init__
def new_init(self, *args, **kwargs):
# 在初始化时打印消息
print(f"Initializing an instance of {cls.__name__}")
# 调用原始的 __init__ 方法
original_init(self, *args, **kwargs)
# 添加一个新的属性
self.extra_info = "This is some extra info."
# 替换原始的 __init__ 方法
cls.__init__ = new_init
return cls
@modify_init
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name} and {self.extra_info}."
# 创建 Person 的实例
person = Person("Alice")
# 调用 greet 方法
print(person.greet())
输出:
- 类装饰器
modify_init
:修改了Person
类的__init__
方法,以便在实例化时打印消息,并添加一个新的属性extra_info
。- 使用装饰器:使用
@modify_init
装饰器,使得Person
类的行为得到了增强。