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 timesE)实现类装饰器
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) # TrueF)实现方法装饰器
这些是专门为类方法设计的:
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 程序员工具库中的宝贵工具,尤其是在使用框架或构建可重用组件时。