装饰器
在 Python 中,装饰器(Decorator) 是一种非常强大的工具,它允许你在不修改函数本身的情况下,动态地增加或修改函数的行为。装饰器广泛应用于函数的增强、权限验证、日志记录、缓存、计时等场景。
文章目录
1. 装饰器的基本概念
装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。返回的新函数通常是对原始函数的增强版,具有某些额外的功能。
装饰器通常用于“包装”函数或方法,使得对原始函数的调用可以自动添加一些功能。
语法:
def decorator(func):
def wrapper():
# 装饰器可以在这里增加代码
print("Before function call")
func() # 调用原始函数
print("After function call")
return wrapper
2. 使用装饰器的基本例子
首先,我们定义一个简单的装饰器,来观察它如何增强原始函数。
案例:
# 定义一个装饰器
def my_decorator(func):
def wrapper():
print("Before function call")
func() # 调用原始函数
print("After function call")
return wrapper
# 使用装饰器
@my_decorator
def greet():
print("Hello, world!")
greet()
输出:
Before function call
Hello, world!
After function call
说明:
@my_decorator是装饰器的简写,它相当于执行greet = my_decorator(greet)。- 当调用
greet()时,实际执行的是装饰器返回的wrapper()函数,而不是原始的greet()函数。 wrapper()中的func()调用原始的greet()函数,前后都增加了额外的行为(打印日志)。
3. 带参数的装饰器
大多数情况下,我们希望装饰器能够处理带有参数的函数。装饰器的内部 wrapper 函数需要能够接收并传递这些参数。
示例:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs) # 调用原始函数并传递参数
print("After function call")
return result # 返回原始函数的结果
return wrapper
@my_decorator
def add(a, b):
return a + b
print(add(3, 5)) # 输出:Before function call 8 After function call
说明:
*args和**kwargs允许wrapper()函数接收并传递任意数量的位置参数和关键字参数。- 装饰器通过传递这些参数来调用原始函数,并在调用前后增加自定义行为。
4. 带返回值的装饰器
装饰器不仅可以增强函数的行为,还可以控制函数的返回值。例如,在装饰器中修改原始函数的返回值。
案例:
def add_one_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result + 1 # 在原始函数返回值的基础上加 1
return wrapper
@add_one_decorator
def add(a, b):
return a + b
print(add(3, 5)) # 输出:9
说明:
- 装饰器修改了
add()函数的返回值,在其基础上增加了 1。
5. 使用装饰器的多个函数
你可以为多个函数使用相同的装饰器,也可以为一个函数叠加多个装饰器。多个装饰器按从下到上的顺序执行。
案例:
def decorator_one(func):
def wrapper(*args, **kwargs):
print("Decorator One Before")
result = func(*args, **kwargs)
print("Decorator One After")
return result
return wrapper
def decorator_two(func):
def wrapper(*args, **kwargs):
print("Decorator Two Before")
result = func(*args, **kwargs)
print("Decorator Two After")
return result
return wrapper
@decorator_one
@decorator_two
def greet(name):
print(f"Hello {name}")
greet("Alice")
输出:
Decorator One Before
Decorator Two Before
Hello Alice
Decorator Two After
Decorator One After
说明:
greet函数先被decorator_two包装,然后被decorator_one包装。两个装饰器按顺序执行,最内层的装饰器先执行。
6. 装饰器带参数
有时,我们希望装饰器能够接受参数,以便灵活地控制装饰器的行为。可以通过在装饰器外面再加一层函数来实现这一点。
案例:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello")
say_hello() # 输出:Hello Hello Hello
说明:
- 装饰器
repeat接受参数n,并返回一个新的装饰器。 - 这个装饰器会调用原始函数
n次。在此例中,say_hello会被调用三次。
7. 内置装饰器
Python 提供了一些内置的装饰器,可以直接使用,最常见的有:
@staticmethod:将方法转换为静态方法。@classmethod:将方法转换为类方法。@property:将方法转换为属性(不需要显式调用)。
案例:使用 @staticmethod
class MyClass:
@staticmethod
def greet():
print("Hello from static method")
MyClass.greet() # 输出:Hello from static method
案例:使用 @classmethod
class MyClass:
@classmethod
def greet(cls):
print(f"Hello from class method {cls}")
MyClass.greet() # 输出:Hello from class method <class '__main__.MyClass'>
案例:使用 @property
class MyClass:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
obj = MyClass("Alice")
print(obj.name) # 输出:Alice
obj.name = "Bob"
print(obj.name) # 输出:Bob
8. 函数的元数据
使用装饰器时,装饰器返回的新函数 wrapper 会覆盖原始函数的一些元数据(如函数名、文档字符串)。为了解决这个问题,可以使用 functools.wraps 来保持原始函数的元数据。
案例:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def greet():
"""This is a greet function."""
print("Hello")
print(greet.__name__) # 输出:greet
print(greet.__doc__) # 输出:This is a greet function.
说明:
- 使用
@wraps(func)保留了greet函数的名称和文档字符串,避免了装饰器修改这些信息。
9. 装饰器的应用场景
装饰器的应用非常广泛,常见的应用场景包括:
- 日志记录:在函数调用前后记录日志。
- 权限校验:在执行某些操作前,验证用户权限。
- 缓存:缓存函数的结果,以提高性能。
- 计时:测量函数执行时间。
- 输入校验:在函数执行前检查输入是否合法。
案例:计时装饰器
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time} seconds to execute.")
return result
return wrapper
@timer
def long_running_function():
time.sleep(2)
long_running_function()
输出:
Function long_running_function took 2.000123456 seconds to execute.
python学习专栏导航
(1)100天python从入门到拿捏《Python 3简介》
(2)100天python从入门到拿捏《python应用前景》
(3)100天python从入门到拿捏《数据类型》
(4)100天python从入门到拿捏《运算符》
(5)100天python从入门到拿捏《流程控制语句》
(6)100天python从入门到拿捏《推导式》
(7)100天python从入门到拿捏《迭代器和生成器》
(8)100天python从入门到拿捏《函数和匿名函数》
11万+

被折叠的 条评论
为什么被折叠?



