Python装饰器:综合指南
当我开始使用 Python 编程时,如果我没记错的话,版本是 3.3。因此,当我开始编程时,装饰器已经在 Python 社区中存在很长时间了。
函数装饰器在 Python 2.2 版本中出现,类装饰器在 Python 2.6 版本中出现。
就我个人而言,我认为 Python 的装饰器功能是该语言的一个非常强大的功能。
实际上,我的目的是写一系列关于 Python 中最难理解的主题的文章。我计划逐一介绍这些主题(大约十多个)。
在本文中,我将尽可能涉及装饰器主题的每个部分。
1. 历史背景
2.什么是装饰器?
本质上,装饰器是 Python 中的一种设计模式,它允许您修改函数或类的行为而不更改其核心结构。装饰器是一种元编程形式,您本质上是在编写操纵其他代码的代码。
您知道 Python 使用以下顺序给出的范围来解析名称:
装饰器处于封闭范围之内,这与闭包概念密切相关。
**关键思想**:装饰器以函数作为输入,为其添加一些功能,并返回修改后的函数。
**类比**:将装饰器想象成礼品包装器。您有一份礼物(原始功能),并用装饰纸(装饰器)包裹它,使其看起来更漂亮或添加额外的功能(如蝴蝶结或卡片)。里面的礼物保持不变,但其呈现方式或相关操作得到了增强。
A)装饰器变体:基于函数与基于类
Python 中的大多数装饰器都是使用函数实现的,但您也可以使用类创建装饰器。
基于函数的装饰器更常见且更简单,而基于类的装饰器提供了额外的灵活性。
基于函数的基本装饰器语法
def my_decorator(func): def wrapper(*args, **kwargs): # Do something before calling the decorated function print("Before function call") result = func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result return wrapper @my_decorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
**解释:**
基于类的基本装饰器语法
这些使用类而不是函数来定义装饰器。
class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # Do something before calling the decorated function print("Before function call") result = self.func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result @MyDecorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
**解释:**
B)实现一个简单的装饰器
装饰器的基本概念是,它们是以另一个函数作为参数并扩展其行为而不显式修改它的函数。
这是最简单的形式:
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 # Using the decorator with @ syntax @my_decorator def say_hello(): print("Hello!") # When we call say_hello() say_hello() # This is equivalent to: # say_hello = my_decorator(say_hello)
C)实现带参数的装饰器
让我们创建一个装饰器来记录函数的执行时间:
def decorator_with_args(func): def wrapper(*args, **kwargs): # Accept any number of arguments print(f"Arguments received: {args}, {kwargs}") return func(*args, **kwargs) # Pass arguments to the original function return wrapper @decorator_with_args def greet(name, greeting="Hello"): print(f"{greeting}, {name}!") greet("Alice", greeting="Hi") # Prints arguments then "Hi, Alice!"
D)实现参数化装饰器
这些是可以接受自己的参数的装饰器:
def repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(times=3) def greet(name): print(f"Hello {name}") return "Done" greet("Bob") # Prints "Hello Bob" three times
E)实现类装饰器
def singleton(cls): instances = {} def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class DatabaseConnection: def __init__(self): print("Initializing database connection") # Creating multiple instances actually returns the same instance db1 = DatabaseConnection() # Prints initialization db2 = DatabaseConnection() # No initialization printed print(db1 is db2) # True
F)实现方法装饰器
这些是专门为类方法设计的:
def debug_method(func): def wrapper(self, *args, **kwargs): print(f"Calling method {func.__name__} of {self.__class__.__name__}") return func(self, *args, **kwargs) return wrapper class MyClass: @debug_method def my_method(self, x, y): return x + y obj = MyClass() print(obj.my_method(5, 3))
G)实现装饰器链
可以将多个装饰器应用于单个函数:
def bold(func): def wrapper(): return "" + func() + "" return wrapper def italic(func): def wrapper(): return "" + func() + "" return wrapper @bold @italic def greet(): return "Hello!" print(greet()) # Outputs: Hello!
**解释:**
H)如果我们不使用@functools.wraps会发生什么?
`functools.wraps` 装饰器(参见文档)是一个辅助函数,当您使用装饰器包装原始函数时,它会保留原始函数的元数据(如其名称、文档字符串和签名)。如果您不使用它,您将丢失这些重要信息。
**例子:**
def my_decorator(func): def wrapper(*args, **kwargs): """Wrapper docstring""" return func(*args, **kwargs) return wrapper @my_decorator def my_function(): """My function docstring""" pass print(my_function.__name__) print(my_function.__doc__)
**输出:**
wrapper Wrapper docstring
**问题:**
**解决方案:使用 functools.wraps):**
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): """Wrapper docstring""" return func(*args, **kwargs) return wrapper @my_decorator def my_function(): """My function docstring""" pass print(my_function.__name__) print(my_function.__doc__)
**输出:**
my_function My function docstring
functools.wraps 的好处:
一、具有状态的装饰器
装饰器还可以在函数调用之间保持状态。这对于缓存或计数函数调用等场景特别有用。
python def count_calls(func): def wrapper(*args, **kwargs): wrapper.calls += 1 print(f"Call {wrapper.calls} of {func.__name__}") return func(*args, **kwargs) wrapper.calls = 0 return wrapper @count_calls def say_hello(): print("Hello!") say_hello() # Call 1 of say_hello say_hello() # Call 2 of say_hello
**输出:**
Call 1 of say_hello Hello! Call 2 of say_hello Hello!
解释:
包装函数维护一个计数器(调用),每次调用被装饰的函数时,该计数器就会递增。
这是一个如何使用装饰器来维护状态的简单示例。
J)Python 装饰器的最佳实践
K)糟糕的实现(反模式)
L)10. 真实世界用例
M)精选 Python 装饰器列表
您可以在下面找到精选的 Python 装饰器列表:
11.结论
装饰器是 Python 中一个强大而优雅的功能,它允许您以干净和声明的方式增强函数和类。
通过了解原则、最佳实践和潜在的陷阱,您可以有效地利用装饰器来编写更模块化、更可维护、更具表现力的代码。
它们是任何 Python 程序员工具库中的宝贵工具,尤其是在使用框架或构建可重用组件时。