python装饰器(Decorator) v1
-
- 简述装饰器
-
- 常见的装饰器
-
- 装饰器的应用
-
- 总结
1. 简述装饰器:
1.1 装饰器的功能
装饰器可以让其他函数或类在不需要做额外代码修改的前提下,在代码运行期间动态增加功能的方式。(运行时,动态的给函数或者类添加功能。)
1.2 装饰器的本质
装饰器本质上是一个接受函数作为参数的可调用对象(通常是函数),并返回一个新的函数对象。这个新的函数对象通常会包装或扩展原始函数的行为。(装饰器本质上是一个python函数或类。)
装饰器的返回值也是一个函数/类对象。
1.3 装饰器的应用
装饰器常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
1.4 装饰器的语法
装饰器是语法糖:利用更简洁流畅的语法实现更为复杂的功能。
使用@ 函数名/类名 可以实现给绑定函数/类额外功能。
1.5 装饰器的引导
# 将函数存储在变量中
def print_message(message):
print(f'send message:{message}')
# 变量名引用函数名
send_message = print_message
send_message('Hi, python!') # send message:Hi, python!
print(type(send_message), type(print_message)) # <class 'function'> <class 'function'>
# 相同的地址
print(id(send_message), id(print_message)) # 2664582954528 2664582954528
# 将函数作为参数传递给另外一个函数
def return_message(message):
return f'send message:{message}'
def call_message(func, message):
print(func(message))
call_message(return_message, 'Hi, python!') # send message:Hi, python!
# 函数嵌套
def func(outer_message):
def print_message(message):
print(f'print message:{message}')
return print_message(outer_message)
func('Hi, python!') # print message:Hi, python!
# 闭包
def outer():
def inner(message):
print(f'print message:{message}')
return inner
send_message = outer()
send_message('Hi, python!') # print message:Hi, python!
# 装饰器
def my_decorator(func):
def wrapper():
print('my_decorator_wrapper')
func()
return wrapper
@ my_decorator # 或 @my_decorator
def print_message():
print('Hi, python!')
print_message()
执行结果:
my_decorator_wrapper
Hi, python!
# 通过函数嵌套实现同样的功能
def my_decorator(func):
def wrapper():
print('my_decorator_wrapper')
func()
return wrapper
def print_message():
print('Hi, python!')
func = my_decorator(print_message)
func()
2. 常见的装饰器
Python中常见的装饰器:函数装饰器、类装饰器、属性装饰器、方法装饰器。
2.1 函数装饰器是最常见的装饰器:
# 记录函数的执行时间
import time
def timer(func):
def wrapper(*args, **kwargs):
startTime = time.time()
res = func()
endTime = time.time()
print(f'执行时间:{endTime - startTime:.2f}秒') # 计算函数的执行时间
return res
return wrapper
@ timer
def myFunction():
time.sleep(1)
print('函数执行完毕')
myFunction()
执行结果:
函数执行完毕
执行时间:1.00秒
2.2 类装饰器
# 为类添加一个name的属性
def addName(cls):
cls.name = 'MyClass' # 为类添加属性并赋值
return cls
@ addName
class MyClass:
pass
print(MyClass.name)
执行结果:
MyClass
2.3 属性装饰器(装饰类的属性,在不改变原属性定义的情况下,为属性添加一些额外的功能):
# 添加类属性检查功能
def typeCheck(attrType):
def decorrator(func):
def wrapper(self, value):
if not isinstance(value, attrType): # 检测类型(int)
raise ValueError(f'属性必须是 {attrType} 类型')
func(self, value)
return wrapper
return decorrator
class MyClass:
@ typeCheck(int)
def setValue(self, value):
self._value = value
myObj = MyClass()
myObj.setValue(10)
myObj.setValue('python') # 抛出异常 # ValueError: 属性必须是 <class 'int'> 类型
2.4 方法装饰器(装饰类的方法):
# 为类方法添加缓存
def cache(func):
cacheDict = {} # 缓存数据
def wrapper(self, *args):
key = (func.__name__, args)
if key in cacheDict:
print('从缓存中读取数据')
return cacheDict[key]
print('计算结果并缓存')
res = func(self, *args)
cacheDict[key] = res
return res
return wrapper
class MyClass:
@cache
def getValue(self, x):
return x ** 2
myObj = MyClass()
print(myObj.getValue(2))
print(myObj.getValue(2))
执行结果:
计算结果并缓存
4
从缓存中读取数据
4
2.5 python常见的内部装饰器:
2.5.1静态方法装饰器@staticmethod
静态方法是一种特殊类型的方法,它不需要特殊的第一个参数(如实例方法需要self
或类方法需要cls
)。静态方法可以通过在方法定义前加上@staticmethod
装饰器来创建。
class MyClass:
@staticmethod
def my_static_method(arg1, arg2):
"""这是一个静态方法,它不接受特殊的第一个参数。"""
return arg1 + arg2
def my_instance_method(self, arg1):
"""这是一个实例方法,它接受'self'作为第一个参数。"""
return self.my_static_method(arg1, 10)
# 使用静态方法
result = MyClass.my_static_method(5, 7)
print(result) # 输出:12
# 创建类的实例并使用实例方法
instance = MyClass()
result_instance = instance.my_instance_method(3)
print(result_instance) # 输出:13
静态方法在功能上类似于模块级别的函数,但是它们被包含在类的命名空间中,这有助于组织代码并提供一种将相关函数分组的方式。静态方法通常用于执行与类无关的操作,这些操作不需要访问或修改类的状态。
值得注意的是,静态方法也可以被类的实例调用,但是它们不会接收到实例引用作为第一个参数,因此它们不能访问或修改实例的状态。如果需要在静态方法中访问类级别的状态,可以显式地传递类名或使用MyClass.
前缀来引用类属性。
2.5.2 类方法装饰器@classmethod
类方法是一种特殊类型的方法,它使用@classmethod
装饰器来定义,并且第一个参数总是类本身,通常用cls
作为参数名。类方法可以在不创建类实例的情况下调用,并且可以访问和修改类级别的状态。
class MyClass:
class_variable = "I am a class variable"
@classmethod
def my_class_method(cls, arg1):
"""这是一个类方法,它接受'cls'作为第一个参数,可以访问和修改类变量。"""
print(cls.class_variable)
cls.class_variable = "Class variable has been changed"
return arg1 * 2
# 使用类方法
result = MyClass.my_class_method(5)
print(result) # 输出:10
# 检查类变量是否已被修改
print(MyClass.class_variable) # 输出:Class variable has been changed
# 创建类的实例并调用类方法
instance = MyClass()
print(instance.class_variable) # 输出:Class variable has been changed
instance_result = instance.my_class_method(3)
print(instance_result) # 输出:6
print(instance.class_variable) # 输出:Class variable has been changed
MyClass.class_variable = 'I am a class variable'
print(MyClass.class_variable) # 输出:I am a class variable
print(instance.class_variable) # 输出:I am a class variable
instance.class_variable = 'instance.class_variable'
print(MyClass.class_variable) # 输出:I am a class variable
print(instance.class_variable) # 输出:instance.class_variable
类方法通常用于执行与类相关但不依赖于特定实例的操作,例如工厂方法模式、修改类级别的状态或访问类级别的配置。它们也可以在元类编程中非常有用,因为它们可以修改类的定义。
要注意的是,尽管类方法可以通过实例调用,但它们仍然接收类本身作为第一个参数,而不是实例。如果你需要在方法中访问实例状态,那么应该使用实例方法。
2.5.3 描述符装饰器@property
它用于将一个方法变成属性调用的形式。在类定义中,可以使用 @property 装饰器来定义 getter 方法,允许像访问数据属性一样访问方法。setter 方法装饰器@<property_name>.setter。deleter 方法装饰器@<property_name>.deleter。
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""Getter for radius."""
return self._radius
@radius.setter
def radius(self, value):
"""Setter for radius."""
if value < 0:
raise ValueError("Radius cannot be negative!")
self._radius = value
@radius.deleter
def radius(self):
del self._radius
@property
def diameter(self):
"""Compute the diameter based on the radius."""
return 2 * self._radius
# 使用 @property 装饰器
circle = Circle(5)
print(circle.radius) # 输出:5,调用了 getter 方法
# 尝试设置 radius 属性
circle.radius = 10 # 调用了 setter 方法
print(circle.radius) # 输出:10
# 尝试获取 diameter 属性
print(circle.diameter) # 输出:20,调用了 diameter 的 getter 方法
# 尝试删除 radius 属性(没有定义 deleter,将会引发 AttributeError)
# del circle.radius
radius 是一个带有 getter 和 setter 方法的属性。你可以像访问普通属性一样访问它,但实际上是在调用方法。diameter 是一个只有 getter 方法的属性,所以它是只读的。
使用 @property 装饰器的好处是,你可以在不改变代码调用方式的情况下,轻松地添加额外的逻辑到属性的访问和修改过程中。可以在 setter 方法中添加验证逻辑,或者在 getter 方法中添加缓存逻辑。
2.5.4 抽象方法装饰器(abc.abstractmethod)@abstractmethod
它用于指示一个方法是抽象的,即这个方法在抽象基类中不应该有具体的实现。子类在继承这个抽象基类时,必须提供这个抽象方法的具体实现。如果没有实现,子类也将被视为抽象类,不能被直接实例化。@abstractmethod 是 Python 的 abc(抽象基类)模块中的一个装饰器。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
"""计算形状的面积"""
pass
@abstractmethod
def perimeter(self):
"""计算形状的周长"""
pass
# 一个遵循 Shape 接口的具体类
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * (self.radius ** 2)
def perimeter(self):
return 2 * 3.14159 * self.radius
# 另一个遵循 Shape 接口的具体类
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
# 尝试实例化抽象基类 Shape 会引发 TypeError
# shape = Shape()
# 实例化具体类 Circle 和 Rectangle 是可以的
circle = Circle(5)
print(circle.area()) # 输出:78.53975
print(circle.perimeter()) # 输出:31.4159
rectangle = Rectangle(4, 6)
print(rectangle.area()) # 输出:24
print(rectangle.perimeter()) # 输出:20
Shape 是一个抽象基类,它定义了两个抽象方法 area 和 perimeter。Circle 和 Rectangle 是 Shape 的子类,它们分别实现了这两个方法。如果你尝试直接实例化 Shape 类,Python 会抛出一个 TypeError,因为 Shape 是一个抽象基类,不能被直接实例化。
使用 @abstractmethod 可以确保你的代码更加健壮,因为它强制要求子类提供必要的方法实现。
3. 装饰器的应用
装饰器的应用:函数装饰函数、函数装饰类、类装饰函数、类装饰类。
# 函数装饰函数
import functools
def outer(func):
@functools.wraps(func) # 用于保存被装饰的类或函数名
def wrapper(*args, **kwargs):
print('wrapper')
return func(*args, **kwargs)
return wrapper
@outer
def func():
pass
func() # wrapper
# 函数装饰类
def outer(cls):
@functools.wraps(cls)
def wrapper(*args, **kwargs):
print('wrapper')
return cls(*args, **kwargs)
return wrapper
@outer
class Foo:
pass
f = Foo() # wrapper
# 类装饰函数:定义一个类装饰器,装饰函数,默认调用__call__方法
class Wrap:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs): # 类装饰器要实现__call__方法
print('Wrap', self._func.__name__)
return self._func(*args, **kwargs)
@Wrap
def func():
pass
func() # Wrap func
# 通过类方法实现装饰器
class Wrap:
def wrapFunc(self):
print('wrapFunc')
return self
@Wrap.wrapFunc
def func():
pass
func() # wrapFunc
# 带计数的装饰器
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f'num of calls is:{self.num_calls}')
return self.func(*args, **kwargs)
@Count # 等价于example = Count(example)
def example():
print('Hi, python!')
example()
执行结果:
num of calls is:1
Hi, python!
example()
执行结果:
num of calls is:2
Hi, python!
# 类装饰类
class Wrap:
def __init__(self, cls):
self._cls = cls
def __call__(self, *args, **kwargs):
print('Wrap')
return self._cls(*args, **kwargs)
@Wrap
class C:
pass
c = C()
# 装饰器通用示例
import functools
def my_decorator(func):
# 保留原函数的元信息(原函数的元信息拷贝到对应的装饰器函数里)
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Before calling the function")
result = func(*args, **kwargs)
print("After calling the function")
return result
return wrapper
# 在使用装饰器的时候名字才会被替换,add.__name__就会变成wrapper。使用functools.wraps(func)保存元信息。
@my_decorator
def add(x, y):
return x + y
print(add.__name__) # add
'''
如果不使用@functools.wraps(func)
print(add.__name__) # wrapper
'''
带参数的装饰器:
# 带参数的函数装饰器(在增加一层嵌套)
def repeat(num): # 接收装饰器参数,@repeat(3)
def my_decorator(func): # 接收装饰器函数/类名
@functools.wraps(func)
# 接收要执行函数参数,print_message(*args, **kwargs)
def wrapper(*args, **kwargs):
for i in range(num):
func(*args, **kwargs)
return wrapper
return my_decorator
@repeat(3) # 传入循环执行的次数
def print_message():
print('Hi, python!')
print_message()
print(print_message.__name__)
执行结果:
Hi, python!
Hi, python!
Hi, python!
print_message
# 带参数的类装饰器
class Count:
# 接收装饰器的参数,Count(*args,**kwargs)
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
self.num_calls = 0
def __call__(self, func): # 接收函数或类名
@functools.wraps(func)
# 接收要装饰的函数或类的参数,example(*args, **kwargs)
def wrapper(*args, **kwargs):
print('self.args:', self.args)
print('self.kwargs:', self.kwargs)
self.num_calls += 1
print(f'num of calls is:{self.num_calls}')
return func(*args, **kwargs)
return wrapper
@Count('aaa')
def example():
print('Hi, python!')
example()
print(example.__name__)
执行结果:
self.args: ('aaa',)
self.kwargs: {}
num of calls is:1
Hi, python!
example
# 描述符装饰器
class MyClass:
@property
def value(self):
print('MyClass().value')
@value.setter
def value(self, message):
print(message)
obj = MyClass()
obj.value # MyClass().value
obj.value = 'Hi, python!' # Hi, python!
obj.value # MyClass().value
# 等价于:
class MyClass:
def get_value(self):
print('MyClass().value')
def set_value(self, message):
print(message)
def del_value(self):
pass
value = property(get_value, set_value, del_value, 'value...')
obj = MyClass()
obj.value # MyClass().value
obj.value = 'Hi, python!' # Hi, python!
obj.value # MyClass().value
print('doc:', MyClass.value.__doc__) # doc: value...
# 自定义实现描述符装饰器(__get__()、__set__()、__delete__())
class MyProperty:
def __init__(self, func):
self.func = func
self.name = func.__name__
self.cache = {} # 缓存装饰器
def __get__(self, instance, owner):
if instance is None:
return self
if self.name not in self.cache:
value = self.func(instance)
print('value:', value)
print('self.cache:', self.cache)
self.cache[self.name] = value
print('self.cache:', self.cache)
return self.cache[self.name]
def __set__(self, instance, value):
print(value)
self.cache[self.name] = value
class MyClass:
@MyProperty
def value(self):
print('MyClass().v`alue')
print('--'*5)
obj = MyClass()
obj.value # MyClass().value
# 修改了value的指向,self.cache[self.name]
obj.value = 'Hi, python!' # Hi, python!
print(obj.value) # Hi, python!
# print(obj.zz) # AttributeError: 'MyClass' object has no attribute 'zz'
执行结果:
----------
MyClass().value
value: None
self.cache: {}
self.cache: {'value': None}
Hi, python!
Hi, python!
print('--'*5)
obj1 = MyClass()
obj1.value # 从缓存取数据self.cache
print(obj1.value) # Hi, python!
obj1.value = 'obj1.value' # obj1.value
print(obj.value) # obj1.value (从缓存取数据,self.cache)
执行结果:
----------
Hi, python!
obj1.value
obj1.value
# 装饰类的装饰器 (输入是类,输出也是类)
import time
# timer_decorator装饰器接收一个类作为参数,并返回一个继承自原始类的新类TimerClass。TimerClass中重写了__getattribute__方法,在调用类的方法时,会计算方法的执行时间并进行打印。
def timer_decorator(cls):
class TimerClass(cls):
def __getattribute__(self, item):
attribute = object.__getattribute__(self, item)
if callable(attribute):
def wrapped_method(*args, **kwargs):
start_time = time.time()
result = attribute(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f'Method {item} exectued in {execution_time} sec-onds.')
return result
return wrapped_method
else:
return attribute
return TimerClass
@timer_decorator
class MyClass:
def my_method(self):
time.sleep(2)
print('Executing my_method')
@timer_decorator
class MyClass1:
def my_method(self):
time.sleep(2)
print('Executing my_method')
def print_message(self):
print('message')
obj = MyClass()
obj.my_method()
obj = MyClass1()
obj.my_method()
obj.print_message()
输出结果:
Executing my_method
Method my_method exectued in 2.0033464431762695 seconds.
Executing my_method
Method my_method exectued in 2.0055508613586426 seconds.
message
Method print_message exectued in 0.0 seconds.
# functools.partial
import time
import functools
class DelayFunc:
def __init__(self, duration, func):
self.duration = duration
self.func = func
def __call__(self, *args, **kwargs):
print(f'Wait for {self.duration} seconds...')
time.sleep(self.duration)
return self.func(*args, **kwargs)
def eager_call(self, *args, **kwargs):
print('Call without delay')
return self.func(*args, **kwargs)
def delay(duration):
"""装饰器:推迟某个函数的执行。同时提供 .eager_call 方法立即执行
"""
# 此处为了避免定义额外函数,直接使用 functools.partial 帮助构造
# DelayFunc 实例
return functools.partial(DelayFunc, duration)
@delay(duration=2)
def add(a, b):
return a + b
# 这次调用将会延迟 2 秒
res = add(1, 2)
print(res)
# 这次调用将会立即执行
res = add.eager_call(1, 2)
print(res)
执行结果:
Wait for 2 seconds...
3
Call without delay
3
# 返回值指向其他函数的装饰器
def func1():
print('func1')
def func2(func):
return func1
@func2
def func3():
print('func3')
func3() # func1
# 不会执行func3里面的print()
# 装饰器的嵌套
import functools
def my_decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator1')
result = func(*args, **kwargs)
print('end execute decorator1')
return result
return wrapper
def my_decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator2')
result = func(*args, **kwargs)
print('end execute decorator2')
return result
return wrapper
@my_decorator1
@my_decorator2
def print_message(message):
print(message)
print_message('Hi, Python!')
执行结果:
execute decorator1
execute decorator2
Hi, Python!
end execute decorator2
end execute decorator1
装饰器执行的嵌套顺序:从内到外定义,从外到内执行。
# 嵌套装饰器的执行顺序
def outer_decorator(func):
def wrapper():
print('执行顺序1')
func()
print('执行顺序4')
return 2
return wrapper
def inner_decorator(func):
def wrapper():
print('执行顺序2')
func()
print('执行顺序3')
return 1
return wrapper
@outer_decorator
@inner_decorator
def my_function():
print("Function is being called")
my_function()
执行结果:
执行顺序1
执行顺序2
Function is being called
执行顺序3
执行顺序4
函数引用装饰器内的属性:
class fun:
funA = 10
funB = 20
def __init__(self, func):
self.func = func
self.funa = 'funaaaaa'
def __call__(self, *args, **kwargs):
self.funb = 'funbbbbb'
print('fun__call__')
result = self.func(*args, **kwargs)
return result
@fun
def f():
"""函数引用装饰器内的属性"""
print(f.funa)
print(f.funb)
print("f")
print('---f')
f()
执行结果:
---f
fun__call__
funaaaaa
funbbbbb
f
- 简单的电商案例
# 电商领域有个功能明显可以使用“策略”模式,即根据客户的属性或订单中的商品计算折扣。
# 有1000或以上积分的顾客,每个订单享5% 折扣。
# 同一订单中,单个商品的数量达到20个或以上,享10%折扣。
# 订单中的不同商品达到10个或以上,享7%折扣。
# 假定一个订单一次只能享用一个折扣。
from collections import namedtuple
promos = []
def promotion(promo_func):
promos.append(promo_func)
return promo_func
@promotion
def fidelity(order):
"""为积分为1000或以上的顾客提供5%折扣"""
return order.total() * .5 if order.customer.fidelity >= 1000 else 0
@promotion
def bulk_item(order):
"""单个商品为20个或以上时提供10%折扣"""
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * .1
return discount
@promotion
def large_order(order):
"""订单中的不同商品达到10个或以上时提供7%折扣"""
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * .07
return 0
def best_promo(order):
"""选择可用的最佳折扣"""
return max(promo(order) for promo in promos)
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:
def __init__(self, product, quantity, price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
class Order:
def __init__(self, customer, cart, promotions=None):
self.__total = None
self.customer = customer
self.cart = list(cart)
self.promotion = promotions
def total(self):
if not hasattr(self, '__total'):
# LineItem.total()
self.__total = sum(item.total() for item in self.cart)
return self.__total
joe = Customer('John Doe', 1000) # 积分为1000
joe_cart = [LineItem('banana', 4, 5.0), LineItem('apple', 10, 1.5), LineI-tem('watermellon', 5, 5.0)]
joe_order = Order(joe, joe_cart)
print(best_promo(joe_order)) # 30.0 (20+15+25)*0.5
4. 总结
装饰器(Decorator)是 Python 中的一个高级功能,允许你修改或增强函数、方法或类的行为,而不需要修改它们的源代码。装饰器本质上是一个接受函数作为参数的可调用对象(通常是一个函数),并返回一个新的函数对象。这个新的函数对象通常会包装(wrap)或修改原始函数的行为。
-
语法:
-
使用 @decorator_name 语法将装饰器应用于函数、方法或类。
-
装饰器可以应用于定义之前或之后的函数,但通常放在函数定义之前。
-
-
工作原理:
- 当 Python 解释器遇到被装饰的函数时,它会立即执行装饰器函数,并将被装饰的函数作为参数传递给装饰器函数。
- 装饰器函数返回一个“包装器”(wrapper)函数,这个包装器函数通常在被装饰的函数之前和/或之后执行某些操作,然后调用原始函数。
- 原始函数的名称和文档字符串(如果有的话)通常会被复制到包装器函数中,以保持函数的身份不变。
-
用途:
- 日志记录:在函数调用前后记录日志信息。
- 性能测试:测量函数执行的时间。
- 缓存:缓存函数的结果,避免重复计算。
- 权限校验:在访问特定函数之前检查用户权限。
- 事务管理:确保一系列操作要么全部成功,要么全部失败。
- 函数参数校验:在函数调用前验证参数。
-
嵌套装饰器:
- 可以将多个装饰器应用于同一个函数。这通常是通过将多个装饰器函数依次应用于函数来实现的。
- 嵌套装饰器的执行顺序是从内到外(即靠近函数定义的装饰器先执行),但装饰器函数本身的调用顺序是从外到内。
-
类装饰器:
- 除了函数可以作为装饰器之外,类也可以作为装饰器。
- 当类被用作装饰器时,Python 会调用类的 call 方法来执行装饰操作。
-
注意事项:
- 装饰器可能会增加代码的复杂性,因此应该谨慎使用。
- 如果装饰器改变了函数的行为,这可能会使代码更难理解和维护。
- 使用装饰器时要确保它们不会引入意外的副作用或性能问题。
-
使用场景:
- 当你想要在不修改原始函数源代码的情况下增强函数功能时,装饰器是一个很好的选择。
- 在设计框架或库时,装饰器可以作为一种灵活的方式来扩展功能或修改行为。
-
示例:
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper @my_decorator def say_hello(): print("Hello!") say_hello() 执行结果: Something is happening before the function is called. Hello! Something is happening after the function is called.
一切callable的对象都可以被用来实现装饰器。
装饰器执行的嵌套顺序:从内到外定义,从外到内执行。
使用装饰器感到迷茫的时候,可以将装饰器用等价的形式转换。