一、Python装饰器是什么?
Python装饰器是一种向函数或类添加额外功能的技术。装饰器允许我们在修改被装饰的函数或类的代码之前或之后执行某些操作。装饰器可以是我们编写的函数或对象,也可以是Python自带的装饰器。
Python装饰器的本质是函数,它既可以接收函数作为参数,也可以返回函数作为结果。Python装饰器的工作原理是将原函数包装在另一个函数内,在原函数的前后执行一些额外的代码,例如打印日志、校验参数、缓存返回结果等等。如下面例子:
def my_decorator(func):
def wrapper():
print("Before the function is called.")
func()
print("After the function is called.")
return wrapper
上述代码是一个简单的Python装饰器,在这个装饰器中,我们定义了一个函数my_decorator,在这个函数中,我们又定义了一个函数wrapper,wrapper函数将传入的函数参数func作为自己的参数并在前后添加了额外的代码。
二、Python装饰器的语法
Python装饰器的语法使用@符号。我们可以在定义函数的时候使用@符号指定装饰器,这样Python就会自动将函数传递给装饰器函数进行处理。
1. 不带参数的装饰器
不带参数的装饰器的定义语法如下:
def decorator(func):
def wrapper(*args, **kwargs):
# do something before the function is called
result = func(*args, **kwargs)
# do something after the function is called
return result
return wrapper
@decorator
def my_function():
return "Hello, world!"
上述代码定义了一个装饰器函数decorator,这个函数接收一个函数参数func,返回一个函数wrapper。在wrapper函数中,我们首先可以执行一些额外的代码,然后再调用被装饰的函数func,并处理返回结果,最后返回结果。
通过添加@decorator,我们就可以使用decorator装饰my_function函数。这意味着my_function函数现在将在执行前后执行decorator函数定义的步骤。
2. 带参数的装饰器
带参数的装饰器的定义语法如下:
def decorator(*args, **kwargs):
def wrapper(func):
# do something before the function is called
result = func(*args, **kwargs)
# do something after the function is called
return result
return wrapper
@decorator(arg1, arg2, ...)
def my_function():
return "Hello, world!"
上述代码定义了一个带参数的装饰器函数decorator。在这个装饰器中,我们首先可以执行一些额外的代码,然后再调用被装饰的函数func,并处理返回结果,最后返回结果。
通过添加@decorator(arg1, arg2, ...),我们就可以使用decorator装饰my_function函数,并将arg1、arg2等参数传递给decorator函数。
三、经典的Python装饰器
Python提供了许多内置的装饰器,这些装饰器可以让我们轻松地定义常见的功能,例如缓存结果、记录日志、限制调用次数等。接下来,我们将介绍一些最常用的Python装饰器。
1. @classmethod和@staticmethod
Python中的@classmethod和@staticmethod装饰器分别用于修饰类方法和静态方法。
@classmethod可以在不实例化类的情况下调用类方法,它的第一个参数通常被命名为cls,它指向调用类的对象,而不是实例化的对象。例如:
class MyClass:
@classmethod
def my_class_method(cls, arg1, arg2):
pass
@staticmethod虽然也修饰静态方法,但它不需要类或实例作为第一个参数。这意味着,静态方法可以在不实例化类的情况下调用,也可以在实例化后调用。例如:
class MyClass:
@staticmethod
def my_static_method(arg1, arg2):
pass
2. @property和@setter
Python中的@property和@setter装饰器可以简化访问和修改实例变量的方式。@property用于获取实例变量的值,而@setter用于设置实例变量的值。
例如,下面是通过使用@property和@setter实现的Person类:
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
print("Getting name")
return self._name
@name.setter
def name(self, name):
print("Setting name to", name)
self._name = name
通过使用@property,我们可以访问实例变量name的值:
p = Person("Alice")
print(p.name) # 输出 "Getting name" 和 "Alice"
通过使用@setter,我们可以设置实例变量name的值:
p.name = "Bob" # 输出 "Setting name to Bob"
3. @staticmethod和@classmethod的组合使用
在Python中,我们可以同时使用@staticmethod和@classmethod,这对于我们定义更复杂的方法非常有用。
例如,下面是使用@staticmethod和@classmethod定义的Person类:
class Person:
def __init__(self, name):
self.name = name
@classmethod
def from_name_list(cls, name_list):
return [cls(name) for name in name_list]
@staticmethod
def say_hello():
print("Hello, world!")
通过@classmethod,我们可以在不创建实例的情况下,从一个名字列表创建一个实例列表:
names = ["Alice", "Bob", "Charlie"]
people = Person.from_name_list(names)
通过@staticmethod,我们可以创建不依赖于实例的方法,例如,我们可以在任何地方使用say_hello方法:
Person.say_hello() # 输出 "Hello, world!"
四、Python装饰器的应用
Python装饰器常常用于实现以下功能:
控制函数或类的访问权限
调用函数或类的前后执行额外步骤,例如记录日志、计时、校验参数等
为函数或类增加功能,例如缓存返回结果、重试、批量处理、异步等
1. 控制函数或类的访问权限
通过使用装饰器,我们可以轻松地控制函数或类的访问权限。
例如,下面是一个简单的装饰器,用于控制只有在特定用户登录时才能调用函数:
def login_required(func):
def wrapper(*args, **kwargs):
# check if user is logged in
if check_user_login():
return func(*args, **kwargs)
else:
return "Please log in to access this page."
return wrapper
通过使用@login_required装饰器,我们可以将函数限制为只有登录用户才能调用:
@login_required
def my_secret_function():
pass
2. 记录日志和计时
通过使用装饰器,我们可以记录函数或类的日志和执行时间,并保存到日志文件中。
import logging
import time
def log_execution_time(logger: logging.Logger):
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
logger.info(f"Execution time of {func.__name__}: {end -start}s")
return result
return wrapper
return decorator
日志文件将记录每个函数的执行时间:
logger = logging.getLogger(__name__)
@log_execution_time(logger)
def my_function():
pass
3. 缓存返回结果
通过使用装饰器,我们可以轻松地为某个函数添加缓存机制,避免重复执行某些昂贵的计算,节省时间和资源。
import functools
def memoize(func):
cache = func.cache = {}
@functools.wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
else:
result = func(*args)
cache[args] = result
return result
return wrapper
上述代码定义了一个memoize装饰器,这个装饰器会在调用函数之前先检查结果是否已经被缓存,如果已经被缓存,则直接返回缓存的结果,否则执行函数并缓存结果。
通过使用@memoize装饰器,我们可以为任何函数添加缓存功能:
@memoize
def my_function(param):
pass
4. 重试
通过使用装饰器,我们可以在函数执行失败时重新调用函数。
import time
def retry(retries=3, delay=0.1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for i in range(retries + 1):
try:
result = func(*args, **kwargs)
return result
except Exception as e:
if i < retries:
time.sleep(delay)
else:
raise e
return wrapper
return decorator
上述代码定义了一个retry装饰器,这个装饰器会在函数执行失败后重试,最多重试retries次,每次重试间隔delay秒。
通过使用@retry装饰器,我们可以为任何函数添加重试功能:
@retry(retries=3, delay=0.1)
def my_function(param):
pass
总结
Python装饰器是一个非常有用的特性,它使我们可以轻松地向函数和类添加额外的功能,例如缓存、记录日志、控制访问权限等。了解装饰器的原理和语法是非常重要的,它可以帮助我们编写更清晰、更高效的代码。