Python 装饰器是一种用于修改或扩展函数或类的功能的语法结构。装饰器本身是一个函数或类,它接受一个函数或类作为参数,并返回一个新的函数或类。通过将装饰器应用于函数或类,可以在不修改原始函数或类代码的情况下,增强其功能或行为。Python 装饰器体系是 Python 编程语言中的一个核心概念,它提供了一种简洁而强大的方式来实现元编程和面向切面编程。
01
基本结构
⚪️ Python 装饰器体系包括多种类型的装饰器,例如函数装饰器、类装饰器、属性装饰器等。其中,函数装饰器是最常见的一种装饰器,它可以用于修改函数的行为,例如添加日志、计时、缓存等功能。类装饰器可以用于修改类的行为,例如为类添加新的方法、属性、元类等。属性装饰器可以用于修改类的属性的行为,例如添加属性验证、类型转换等。
Python 装饰器的实现方式有多种,包括基于函数的装饰器、基于类的装饰器、基于函数和类的混合装饰器等。Python 装饰器体系的灵活性和强大性使得开发者可以轻松地实现各种功能和扩展,从而提高代码的可复用性和可维护性。
Python 装饰器的基本语法结构如下:
@decorator
def func():
pass
其中,decorator 是一个装饰器函数,它接受一个函数 func 作为参数,并返回一个新的函数。@decorator 语法相当于将 func 函数作为参数传递给 decorator 函数,然后将其返回值重新赋值给 func,从而实现了对 func 函数的装饰。下面举个函数的例子:
# 定义函数happy,输出打印
def happy():
print('你好,我很开心!')
# 调用函数happy
happy()
# 把函数happy赋予happiness
happiness = happy
# 调用happiness
happiness()
# 删除happy,此时happy无法调用
del happy
# 但是happiness可以被调用
happiness()
你会发现定义函数happy这个函数变量可以直接赋予给happiness,即使是happy被删除,happiness也可以调用,这就是Python中把函数看作是对象,相当于把happy这个对象复制给了happiness,即使被删除,复制出的对象也不会消失。
函数中定义函数
函数中可以定义函数,在函数内部中定义的函数相当于内部的局部变量,只能在内部访问,在函数外部是不能访问的。
# 函数中定义函数
def happy():
print('你好,我很开心!')
def play():
print("玩能使我开心!")
def eat():
print("吃好吃的使我开心!")
play()
eat()
happy()
并且函数中是可以返回函数,现在我们对上面的代码进行修改:
# 函数中返回函数
def happy(name='play'):
print('你好,我很开心!')
def play():
print("玩能使我开心!")
def eat():
print("吃好吃的使我开心!")
if name == 'play':
return play
else:
return eat
# 现在demo变量指向happy中的play函数
demo = happy('play')
# 调用play函数
demo()
根据上面的代码我们思考,能不能把函数当作参数传递给另一个函数:
# 函数中传递函数
def happy():
return "你好,我很开心!"
def func2happy(func):
print("参数是函数")
print(func())
func2happy(happy)
现在你已经具备所有必需知识,来进一步学习装饰器真正是什么了。装饰器让你在一个函数的前后去执行代码。
02
实现第一个装饰器
⚪️ 装饰器就是把曾经的函数或类在原有的基础上添加功能,然后再覆盖曾经的函数或类。
例如根据上面的基础学习,我们把代码继续修改,我们定义了happy函数,还有func2happy函数用于传入参数为函数的函数,在内部定义了demo函数,我们把happy函数传入到func2happy函数中,在demo函数中规划结构进行封装,再把demo函数本身使用return方法整体返回传递给变量a,此时的a就是demo,然后加括号执行demo函数。
# 函数中传递函数
def happy():
return "你好,我很开心!"
def func2happy(func):
def demo():
print("我是传递函数前执行")
print(func())
print("我是传递函数后执行")
return demo
# 返回函数的句柄
a = func2happy(happy)
# 执行返回函数
a()
执行方法可以简化为:
func2happy(happy)() # 简化方式写法
执行结果:
我是传递函数前执行
你好,我很开心!
我是传递函数后执行
上述你会发现根本没有使用@符号,那么如何实现使用@符号进行优化代码呢?
def func2happy(func):
def demo():
print("我是传递函数前执行")
print(func())
print("我是传递函数后执行")
return demo
# 第一个装饰器
@func2happy
def happy():
return "你好,我很开心!"
# 输出函数名
print(happy.__name__) # 输出demo
上述代码使用@进行装饰发现输出的函数名是封装函数的名称,而不是原来函数的名称,Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps
from functools import wraps
def func2happy(func):
@wraps(func) # 新添加wraps方法
def demo():
print("我是传递函数前执行")
print(func())
print("我是传递函数后执行")
return demo
# 第一个装饰器
@func2happy
def happy():
return "你好,我很开心!"
# 输出函数名
print(happy.__name__) # 输出happy
03
使用场景
装饰器是 Python 编程中非常实用的功能,它可以用于修改或扩展函数或类的功能。以下是装饰器的一些常见使用场景:
1. 记录日志:可以使用装饰器来记录函数的调用日志,例如记录函数的输入参数、执行时间、输出结果等信息。 2. 计时器:可以使用装饰器来记录函数的执行时间,以便进行性能优化。 3. 缓存:可以使用装饰器来实现函数的结果缓存,避免重复计算。 4. 验证器:可以使用装饰器来实现函数的参数验证,例如验证参数类型、范围、有效性等。 5. 重试机制:可以使用装饰器来实现函数的重试机制,例如在函数执行失败时自动重试。 6. 授权和身份验证:可以使用装饰器来实现函数的授权和身份验证,例如验证用户是否有访问某个函数的权限。 7. 日志收集:可以使用装饰器来收集多个函数的日志信息,并将其输出到指定的位置,例如文件或数据库。 8. 代码注入:可以使用装饰器来在函数执行前或执行后插入代码,从而实现一些功能扩展,例如打印调试信息、修改函数返回值等。 9. 基于切面的编程:可以使用装饰器来实现基于切面的编程,例如为多个函数添加相同的功能,避免代码重复。
总的来说,装饰器的使用场景非常广泛,可以帮助开发者提高代码的可复用性、可维护性和可扩展性,从而提高开发效率和代码质量。
04
装饰器种类
⚪️ 装饰器可以是自定义的函数,也可以是 Python 内置的装饰器函数或者第三方库中的装饰器函数。Python 内置的装饰器函数包括 @staticmethod、@classmethod、@property 等,它们可以用于修改类中静态方法、类方法和属性的行为。此外,Python 还提供了 functools 模块中的 wraps 装饰器函数,可以用于保留函数的元数据,例如函数名、参数列表等。
Python 内置了一些常用的装饰器函数,它们可以用于修改类或函数的行为,增加一些额外的功能。下面是一些常见的 Python 内置装饰器函数:
1. `@staticmethod`:将一个类中的方法定义为静态方法。静态方法不需要传递实例对象,可以直接通过类名调用,例如 `ClassName.static_method()`。
2. `@classmethod`:将一个类中的方法定义为类方法。类方法可以访问类变量,也可以通过类名调用,例如 `ClassName.class_method()`。
3. `@property`:将一个类中的方法定义为属性,可以像访问属性一样访问该方法,例如 `instance.property_name`。
4. `@abstractmethod`:将一个方法定义为抽象方法,在子类中必须实现该方法。
5. `@wraps`:将一个函数的元数据复制到另一个函数中,避免函数被装饰后丢失元数据。
6. `@lru_cache`:将一个函数的结果缓存到内存中,避免重复计算。该装饰器函数可以接受一个可选的 `maxsize` 参数,用于指定缓存的大小。
7. `@functools.singledispatch`:将一个函数定义为泛型函数,可以根据传入参数类型的不同,调用不同的实现函数。
8. `@functools.lru_cache`:与 `@lru_cache` 类似,将一个函数的结果缓存到内存中,并支持设置缓存的大小和过期时间。
除了上面列举的这些装饰器函数外,Python 还提供了许多其他的内置装饰器函数,可以根据不同的需求选择使用。这些装饰器函数可以帮助开发者快速地实现一些常见的功能,提高代码的可维护性和可读性。