欢迎光临
我们一直在努力

Python 中的装饰器

   装饰器是 Python 中修改或增强函数行为的一种设计模式,它可以在不修改原函数代码的情况下,为函数添加额外功能。装饰器本质上是一个高阶函数,接收一个函数作为参数,返回一个新的函数。

一、装饰器

1. 为什么需要装饰器?

# 原始需求:计算函数执行时间

# 问题:每个函数都要重复写计时代码
def func1():
start = time.time()
# … 函数逻辑
end = time.time()
print(f"耗时: {end – start}")

def func2():
start = time.time()
# … 函数逻辑
end = time.time()
print(f"耗时: {end – start}")

# 装饰器解决方案:复用计时逻辑
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 耗时: {end – start:.2f}s")
return result
return wrapper

@timer
def func1():
pass

@timer
def func2():
pass

2. 装饰器的本质

# 装饰器语法糖
@decorator
def func():
pass

# 等价于
func = decorator(func)

二、函数装饰器

1. 无参数装饰器

def logger(func):
"""记录函数调用的装饰器"""
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
print(f"参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper

@logger
def add(a, b):
return a + b

add(3, 5)
# 输出:
# 调用函数: add
# 参数: args=(3, 5), kwargs={}
# 返回值: 8

2. 带参数的装饰器

def repeat(times):
"""重复执行函数的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for i in range(times):
print(f"第 {i+1} 次执行")
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator

@repeat(3)
def greet(name):
return f"Hello, {name}"

result = greet("Alice")
print(result) # ['Hello, Alice', 'Hello, Alice', 'Hello, Alice']

3. 多层装饰器

def bold(func):
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper

def italic(func):
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper

@bold
@italic
def greet(name):
return f"Hello, {name}"

print(greet("Alice")) # <b><i>Hello, Alice</i></b>

# 执行顺序:从下往上
# greet = bold(italic(greet))

三、类装饰器

1. 类作为装饰器

class CountCalls:
"""统计函数调用次数的装饰器"""
def __init__(self, func):
self.func = func
self.count = 0

def __call__(self, *args, **kwargs):
self.count += 1
print(f"调用次数: {self.count}")
return self.func(*args, **kwargs)

@CountCalls
def say_hello():
print("Hello!")

say_hello() # 调用次数: 1 \\n Hello!
say_hello() # 调用次数: 2 \\n Hello!

2. 带参数的类装饰器

class Repeat:
def __init__(self, times):
self.times = times

def __call__(self, func):
def wrapper(*args, **kwargs):
results = []
for i in range(self.times):
results.append(func(*args, **kwargs))
return results
return wrapper

@Repeat(3)
def greet(name):
return f"Hello, {name}"

print(greet("Alice")) # ['Hello, Alice', 'Hello, Alice', 'Hello, Alice']

四、常用内置装饰器

1. @staticmethod 和 @classmethod

class MyClass:
class_var = "类变量"

@staticmethod
def static_method():
"""静态方法:不需要 self 或 cls"""
return "静态方法"

@classmethod
def class_method(cls):
"""类方法:第一个参数是类本身"""
return f"类方法,访问 {cls.class_var}"

def instance_method(self):
"""实例方法:第一个参数是实例"""
return "实例方法"

print(MyClass.static_method()) # 静态方法
print(MyClass.class_method()) # 类方法,访问 类变量

2. @property

class Circle:
def __init__(self, radius):
self._radius = radius

@property
def radius(self):
"""getter"""
return self._radius

@radius.setter
def radius(self, value):
"""setter"""
if value <= 0:
raise ValueError("半径必须大于0")
self._radius = value

@property
def area(self):
"""只读属性"""
return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius) # 5
c.radius = 10
print(c.area) # 314.159

3. @functools.wraps

保留原函数的元数据(名称、文档字符串等)。

import functools

def my_decorator(func):
@functools.wraps(func) # 保留 func 的元信息
def wrapper(*args, **kwargs):
"""包装函数"""
return func(*args, **kwargs)
return wrapper

@my_decorator
def greet(name):
"""问候函数"""
return f"Hello, {name}"

print(greet.__name__) # greet(有 @wraps)
print(greet.__doc__) # 问候函数(有 @wraps)

# 如果没有 @wraps,会输出 wrapper 和 包装函数

五、装饰器的执行顺序

def decorator_a(func):
print("装饰器 A 开始")
def wrapper(*args, **kwargs):
print("调用 A")
return func(*args, **kwargs)
print("装饰器 A 结束")
return wrapper

def decorator_b(func):
print("装饰器 B 开始")
def wrapper(*args, **kwargs):
print("调用 B")
return func(*args, **kwargs)
print("装饰器 B 结束")
return wrapper

@decorator_a
@decorator_b
def my_func():
print("原函数")

my_func()

装饰器 B 开始
装饰器 B 结束
装饰器 A 开始
装饰器 A 结束
调用 A
调用 B
原函数

执行顺序:

  • 装饰阶段:从下往上(decorator_b 先执行)

  • 调用阶段:从上往下(wrapper_a → wrapper_b → 原函数)

  • 核心要点:

  • 装饰器本质是高阶函数:接收函数,返回函数

  • @decorator 等价于 func = decorator(func)

  • 多个装饰器:装饰时从下往上,调用时从上往下

  • 总是用 @functools.wraps 保留原函数元信息

  • 类装饰器通过 __call__ 方法实现

  • 赞(0)
    未经允许不得转载:171主机测评 » Python 中的装饰器
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址