什么是装饰器?
装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,动态地改变函数的行为。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的核心概念
在Python中,装饰器使用@符号作为前缀,放置在函数定义之前。例如:
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
@my_decorator
def say_hello():
print("Hello!")
say_hello()
输出结果:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
装饰器的类型
1. 函数装饰器
这是最常见的装饰器类型,用于修改或增强函数的行为。
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds to run.")
return result
return wrapper
@timing_decorator
def slow_function():
time.sleep(2)
print("Function completed.")
slow_function()
2. 类装饰器
类装饰器使用类来实现,通常用于需要维护状态的场景。
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
输出:
Call 1 of say_hello
Hello!
Call 2 of say_hello
Hello!
带参数的装饰器
有时我们需要创建可以接受参数的装饰器,这需要额外一层嵌套函数。
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
装饰器的实用场景
1. 日志记录
def log_function_call(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_function_call
def add(a, b):
return a + b
add(3, 5)
2. 权限验证
def requires_admin(func):
def wrapper(user, *args, **kwargs):
if user.get("role") != "admin":
raise PermissionError("Admin access required")
return func(user, *args, **kwargs)
return wrapper
@requires_admin
def delete_user(user, username):
print(f"User {username} deleted by {user['name']}")
admin_user = {"name": "John", "role": "admin"}
regular_user = {"name": "Jane", "role": "user"}
delete_user(admin_user, "bad_actor") # Works
# delete_user(regular_user, "bad_actor") # Raises PermissionError
3. 缓存/记忆化
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # Much faster than without memoization
内置装饰器
Python提供了一些内置装饰器:
1. @property
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("Radius must be positive")
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
2. @classmethod 和 @staticmethod
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
@classmethod
def from_fahrenheit(cls, fahrenheit):
return cls((fahrenheit - 32) * 5/9)
@staticmethod
def is_freezing(celsius):
return celsius <= 0
t1 = Temperature(25)
t2 = Temperature.from_fahrenheit(77)
print(t2.celsius) # 25.0
print(Temperature.is_freezing(5)) # False
高级装饰器技巧
1. 装饰器堆叠
def bold(func):
def wrapper():
return "<b>" + func() + "</b>"
return wrapper
def italic(func):
def wrapper():
return "<i>" + func() + "</i>"
return wrapper
@bold
@italic
def hello():
return "Hello World"
print(hello()) # <b><i>Hello World</i></b>
2. 保留元数据
使用functools.wraps保留原始函数的元数据:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper function"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Example function"""
pass
print(example.__name__) # example
print(example.__doc__) # Example function
3. 类方法装饰器
class MyClass:
def __init__(self):
self._value = 0
@property
def value(self):
return self._value
@value.setter
def value(self, new_value):
self._value = new_value
def __str__(self):
return f"MyClass(value={self._value})"
# 使用装饰器增强类方法
def validate_positive(func):
@wraps(func)
def wrapper(self, value):
if value <= 0:
raise ValueError("Value must be positive")
return func(self, value)
return wrapper
# 动态添加装饰器到类方法
MyClass.value = property(MyClass.value.fget, validate_positive(MyClass.value.fset))
装饰器的性能考虑
虽然装饰器非常强大,但它们也会带来一些性能开销:
import time
from functools import wraps
def heavy_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 模拟一些开销
time.sleep(0.001)
return func(*args, **kwargs)
return wrapper
@heavy_decorator
def fast_function():
return "Fast"
def normal_function():
return "Fast"
# 性能比较
start = time.time()
for _ in range(1000):
fast_function()
decorator_time = time.time() - start
start = time.time()
for _ in range(1000):
normal_function()
normal_time = time.time() - start
print(f"With decorator: {decorator_time:.4f}s")
print(f"Without decorator: {normal_time:.4f}s")
最佳实践
- 保持装饰器简单:每个装饰器应该只做一件事
- 使用functools.wraps:保留原始函数的元数据
- 文档化装饰器:清楚地说明装饰器的行为
- 考虑可读性:复杂的装饰器可能降低代码可读性
- 测试装饰器:确保装饰器在各种情况下都能正常工作
结论
装饰器是Python中一个非常强大的特性,它们提供了一种优雅的方式来扩展和修改函数和方法的行为。通过理解装饰器的工作原理和各种使用场景,你可以编写出更加模块化、可重用和易于维护的代码。记住,装饰器应该被用来增强代码的功能,而不是使其复杂化。当使用得当时,装饰器可以极大地提高代码的质量和可读性。
