什么是装饰器?
装饰器(Decorator)是 Python 中一种强大的语法特性,它允许在不修改原函数代码的情况下,为函数或类添加额外的功能。装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。
装饰器的基本概念
1. 函数是一等公民
在 Python 中,函数是一等公民,这意味着:
- 函数可以作为参数传递给其他函数
- 函数可以作为其他函数的返回值
- 函数可以赋值给变量
python
def greet(name):
return f"Hello, {name}!"
# 函数赋值给变量
my_func = greet
print(my_func("Alice")) # 输出: Hello, Alice!
# 函数作为参数
def call_twice(func, arg):
return func(arg) + " " + func(arg)
print(call_twice(greet, "Bob")) # 输出: Hello, Bob! Hello, Bob!
2. 闭包
闭包是理解装饰器的关键概念。闭包指的是内部函数可以访问外部函数作用域中的变量,即使外部函数已经执行完毕。
python
def outer_function(msg):
def inner_function():
print(msg)
return inner_function
my_func = outer_function("Hello, Closure!")
my_func() # 输出: Hello, Closure!
简单的装饰器实现
基础装饰器
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
def say_hello():
print("Hello!")
# 使用装饰器
decorated_hello = my_decorator(say_hello)
decorated_hello()
输出:
text
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
使用 @ 语法糖
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()
处理带参数的函数
装饰带参数的函数
python
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 greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # 输出: Hello, Alice!
print(greet("Bob", greeting="Hi")) # 输出: Hi, Bob!
保留函数元信息
使用 functools.wraps
可以保留原函数的元信息:
python
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned")
return result
return wrapper
@my_decorator
def add(a, b):
"""Add two numbers"""
return a + b
print(add.__name__) # 输出: add
print(add.__doc__) # 输出: Add two numbers
带参数的装饰器
有时候我们需要装饰器本身也能接受参数:
python
def repeat(num_times):
def decorator_repeat(func):
@functools.wraps(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")
输出:
text
Hello, Alice!
Hello, Alice!
Hello, Alice!
类装饰器
除了函数装饰器,还可以使用类来实现装饰器:
作为装饰器的类
python
class CountCalls:
def __init__(self, func):
functools.update_wrapper(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() # 输出: Call 1 of say_hello \n Hello!
say_hello() # 输出: Call 2 of say_hello \n Hello!
带参数的类装饰器
python
class DecoratorWithArgs:
def __init__(self, prefix):
self.prefix = prefix
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"{self.prefix}: Before function call")
result = func(*args, **kwargs)
print(f"{self.prefix}: After function call")
return result
return wrapper
@DecoratorWithArgs("DEBUG")
def multiply(a, b):
return a * b
print(multiply(3, 4))
装饰器的实际应用场景
1. 日志记录
python
import time
def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
return result
return wrapper
@log_execution_time
def slow_function():
time.sleep(1)
return "Done"
slow_function()
2. 权限验证
python
def require_login(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated', False):
raise PermissionError("User must be logged in")
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
return f"Viewing profile of {user['username']}"
user = {'username': 'alice', 'is_authenticated': True}
print(view_profile(user))
3. 缓存(记忆化)
python
def cache(func):
cached_results = {}
@functools.wraps(func)
def wrapper(*args):
if args in cached_results:
print(f"Returning cached result for {args}")
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
4. 重试机制
python
import random
def retry(max_attempts=3, delay=1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise e
print(f"Attempt {attempts} failed: {e}. Retrying in {delay} seconds...")
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=1)
def unreliable_function():
if random.random() < 0.7:
raise Exception("Something went wrong!")
return "Success!"
print(unreliable_function())
多个装饰器的执行顺序
当使用多个装饰器时,它们按照从下往上的顺序执行:
python
def decorator1(func):
def wrapper():
print("Decorator 1 - before")
func()
print("Decorator 1 - after")
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2 - before")
func()
print("Decorator 2 - after")
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
输出:
text
Decorator 1 - before
Decorator 2 - before
Hello!
Decorator 2 - after
Decorator 1 - after
内置装饰器
Python 提供了一些有用的内置装饰器:
@staticmethod 和 @classmethod
python
class MyClass:
class_attribute = "class value"
def __init__(self, value):
self.instance_attribute = value
@staticmethod
def static_method():
return "This is a static method"
@classmethod
def class_method(cls):
return f"This is a class method. Class attribute: {cls.class_attribute}"
def instance_method(self):
return f"This is an instance method. Instance attribute: {self.instance_attribute}"
print(MyClass.static_method())
print(MyClass.class_method())
obj = MyClass("instance value")
print(obj.instance_method())
@property
python
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 cannot be negative")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
@property
def diameter(self):
return 2 * self._radius
circle = Circle(5)
print(f"Radius: {circle.radius}")
print(f"Area: {circle.area}")
circle.radius = 10
print(f"New area: {circle.area}")
装饰器的最佳实践
- 总是使用 functools.wraps:保留原函数的元信息
- 保持装饰器简单:每个装饰器应该只负责一个明确的功能
- 考虑可测试性:确保装饰后的函数仍然易于测试
- 文档化装饰器:说明装饰器的用途和效果
- 注意执行顺序:多个装饰器的执行顺序很重要
总结
Python 装饰器是一种强大的元编程工具,它允许我们以声明性的方式修改或增强函数和类的行为。通过理解闭包、高阶函数和装饰器语法,你可以创建出简洁、可重用的代码。装饰器在日志记录、权限验证、缓存、性能监控等场景中有着广泛的应用,是每个 Python 开发者都应该掌握的重要概念。
装饰器的学习曲线可能有些陡峭,但一旦掌握,它们将成为你工具箱中不可或缺的强大工具,帮助你编写更加模块化、可维护的 Python 代码。