1. 什么是装饰器
在Python中,装饰器是一种特殊的函数,它可以动态地修改或扩展函数或类的功能。装饰器可以在不改变原始代码的情况下为函数或类添加新功能。
装饰器本质上是一个函数,它可以接收一个函数作为参数并返回一个经过装饰之后的函数。可以把它看作一个“包装器”,它会把原函数包装起来,并创建一个新的函数作为返回值。
例如,下面的代码定义了一个装饰器函数my_decorator
,它把一个函数名作为参数,并返回一个新函数wrapper
,在原函数执行前后会执行一些操作:
def my_decorator(func):
def wrapper():
# 在函数调用之前添加一些操作
print("Before function is called.")
# 调用函数
func()
# 在函数调用之后添加一些操作
print("After function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello, World!")
在这个示例中,我们定义了一个say_hello
函数并使用@my_decorator装饰了它。现在,每当调用say_hello
函数时,它将被my_decorator
装饰器包装,并在调用前后输出一些额外的信息。
2. 装饰器的基本用法
2.1 无参数装饰器
最简单的装饰器是不带任何参数的函数装饰器。例如,下面的代码定义了一个装饰器函数my_decorator
,它可以将一个普通的函数say_hello
转变成在调用前后输出一些额外信息的函数:
def my_decorator(func):
def wrapper():
print("Before function is called.")
func()
print("After function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello, World!")
say_hello()
执行这段代码将输出如下信息:
Before function is called.
Hello, World!
After function is called.
说明my_decorator
装饰器成功地将say_hello
函数包装起来,并在调用前后添加了额外的操作。
2.2 带参数装饰器
有时候我们需要给装饰器传递一些参数,以便对被装饰函数进行不同的处理。下面的示例演示了如何创建一个带参数的装饰器,它可以根据传入的参数来控制输出信息:
def my_decorator(text):
def decorator(func):
def wrapper():
print(f"Before function is called with \"{text}\".")
func()
print(f"After function is called with \"{text}\".")
return wrapper
return decorator
@my_decorator("foo")
def say_hello():
print("Hello, World!")
say_hello()
执行这段代码,将输出如下信息:
Before function is called with "foo".
Hello, World!
After function is called with "foo".
在这个示例中,my_decorator
函数带有一个参数text
,它返回一个装饰器函数decorator
,它又返回一个包装函数wrapper
。当我们使用@my_decorator("foo")
将say_hello
函数装饰起来时,实际上执行的是decorator(say_hello)("foo")()
,这个调用中,text="foo"
,func=say_hello
,包装函数可以在函数调用前后输出一些信息。
2.3 保留原函数元信息
装饰器在装饰函数时可能会丢失一些有用的元信息,比如函数名、文档字符串等。为了保留这些元信息,可以使用内置的functools.wraps
装饰器来装饰包装函数。下面的示例演示了如何使用wraps
装饰器:
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper():
print("Before function is called.")
func()
print("After function is called.")
return wrapper
@my_decorator
def say_hello():
"""A friendly greeting."""
print("Hello, World!")
print(say_hello.__name__)
print(say_hello.__doc__)
运行这段代码将输出如下信息:
say_hello
A friendly greeting.
在这个示例中,我们使用了functools.wraps
装饰器来装饰wrapper
函数,并显式地设置wrapper.__name__=func.__name__
和wrapper.__doc__=func.__doc__
,这样就可以保留原函数的元信息。
3. 装饰器的实际应用
3.1 性能测试
在大型项目中,性能测试是非常重要的一部分。装饰器可以用来简化性能测试的过程,并在不同的测试中自动采用不同的策略。下面的示例演示了如何创建一个性能测试装饰器timed
,它可以测量函数的执行时间:
import time
import random
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"{func.__name__} took {end_time-start_time:.6f} seconds.")
return result
return wrapper
@timed
def random_sort(n):
"""Sort a list of random integers."""
x = [random.randint(0, 1000) for _ in range(n)]
x.sort()
return x
x = random_sort(10000)
在这个示例中,我们定义了一个timed
装饰器,它可以测量函数执行时间并输出结果。我们还定义了一个random_sort
函数并使用@timed
装饰了它。现在,每次调用random_sort
函数时,它都会被timed
装饰器包装,并测量其执行时间。
3.2 认证和授权
认证和授权是许多应用程序中常见的功能。认证用于验证用户的身份,而授权用于限制用户可以执行的操作。装饰器可以用来实现这些功能。下面的示例演示了如何创建一个简单的认证装饰器,它要求用户输入正确的用户名和密码才能执行被装饰函数:
USER_CREDENTIALS = {"admin": "123456"}
def authenticated(func):
@functools.wraps(func)
def wrapper():
username = input("Username: ")
password = input("Password: ")
if username in USER_CREDENTIALS and password == USER_CREDENTIALS[username]:
return func()
else:
raise Exception("Invalid username or password.")
return wrapper
@authenticated
def secret_data():
return "This is secret data."
print(secret_data())
在这个示例中,我们定义了一个authenticated
装饰器,它要求用户输入正确的用户名和密码才能执行被装饰函数。我们还定义了一个secret_data
函数并使用@authenticated
装饰了它。现在,每次调用secret_data
函数时,它都会被authenticated
装饰器包装,并要求用户输入正确的用户名和密码。
3.3 日志记录
日志记录是应用程序中常见的功能之一,装饰器可以用来自动记录函数调用和返回值。下面的示例演示了如何创建一个简单的日志记录装饰器log_call
:
def log_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_call
def say_hello(name):
return f"Hello, {name}!"
print(say_hello("Alice"))
在这个示例中,log_call
装饰器可以自动记录被装饰函数的调用和返回值,我们使用它装饰了say_hello
函数并输出了其调用结果。
4. 结论
通过本文的介绍,我们了解了Python装饰器的概念、基本用法和实际应用。装饰器是Python中非常强大和灵活的语言特性,它可以用于简化代码、增强函数功能、优化性能等多个方面。通过合理地使用装饰器,我们可以实现更简洁、更高效和更可维护的代码。