装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原函数代码的情况下,为函数添加额外的功能。就像化妆师可以为演员添加不同的妆容而不改变演员本身一样,装饰器可以"装饰"函数,为其添加各种"修饰"功能。
装饰器本质上是一个Python函数(或其他可调用对象),它接受一个函数作为输入并返回一个新的函数。通过装饰器,我们可以在不修改原函数代码的情况下,为函数添加日志记录、性能测试、权限校验、缓存等额外功能。
@decorator
def function():
pass
这等同于:
def function():
pass
function = decorator(function)
在Python中,函数是"一等公民",这意味着:
def greet(name):
return f"Hello, {name}!"
# 函数赋值给变量
say_hello = greet
print(say_hello("Alice")) # 输出: Hello, Alice!
高阶函数是指能够接受函数作为参数,或者返回函数作为结果的函数。装饰器就是利用了高阶函数的特性。
def make_adder(n):
def adder(x):
return x + n
return adder
add5 = make_adder(5)
print(add5(3)) # 输出: 8
闭包是指内部函数可以访问外部函数作用域中的变量,即使外部函数已经执行完毕。装饰器通常使用闭包来保存状态。
def outer():
message = "Hello"
def inner():
print(message)
return inner
my_func = outer()
my_func() # 输出: Hello
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.
"""
def decorator_with_args(func):
def wrapper(*args, **kwargs):
print("Decorator is working")
return func(*args, **kwargs)
return wrapper
@decorator_with_args
def greet(name):
return f"Hello, {name}"
print(greet("Alice"))
"""
输出:
Decorator is working
Hello, Alice
"""
有时我们需要装饰器本身也能接受参数:
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("World")
"""
输出:
Hello World
Hello World
Hello World
"""
Python提供了一些内置装饰器,最常用的有:
@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:
self._radius = value
else:
raise ValueError("Radius must be positive")
circle = Circle(5)
print(circle.radius) # 输出: 5
circle.radius = 10
print(circle.radius) # 输出: 10
@classmethod
和 @staticmethod
class MyClass:
@classmethod
def class_method(cls):
print(f"Called class_method of {cls}")
@staticmethod
def static_method():
print("Called static_method")
MyClass.class_method() # 输出: Called class_method of
MyClass.static_method() # 输出: Called static_method
@functools.wraps
当使用装饰器时,原函数的元信息(如__name__
、__doc__
)会被替换为装饰器内部函数的元信息。functools.wraps
可以保留这些元信息:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper docstring"""
print("Something is happening before the function is called.")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""Say hello docstring"""
print("Hello!")
print(say_hello.__name__) # 输出: say_hello
print(say_hello.__doc__) # 输出: Say hello docstring
import time
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds")
return result
return wrapper
@timer
def waste_time(n):
for _ in range(n):
sum([i**2 for i in range(1000)])
waste_time(1000)
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(30)) # 计算结果会被缓存
def requires_admin(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('is_admin'):
return func(user, *args, **kwargs)
else:
raise PermissionError("You must be an admin to perform this action")
return wrapper
@requires_admin
def delete_database(user):
print("Database deleted!")
admin_user = {'username': 'admin', 'is_admin': True}
regular_user = {'username': 'user', 'is_admin': False}
delete_database(admin_user) # 正常执行
delete_database(regular_user) # 抛出PermissionError
def log_activity(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__} with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"Function {func.__name__} returned: {result}")
return result
return wrapper
@log_activity
def add_numbers(a, b):
return a + b
add_numbers(3, 5)
"""
输出:
Calling function add_numbers with args: (3, 5), kwargs: {}
Function add_numbers returned: 8
"""
可以在一个函数上应用多个装饰器,它们会从下往上依次执行:
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()
"""
输出:
Decorator 1 before
Decorator 2 before
Hello!
Decorator 2 after
Decorator 1 after
"""
除了函数装饰器,Python还支持类装饰器。类装饰器是一个接受类作为参数并返回一个新类的可调用对象:
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("Initializing database")
db1 = Database()
db2 = Database()
print(db1 is db2) # 输出: True
@functools.wraps
来保留原函数的元信息装饰器是Python中非常强大的特性,它允许我们:
通过合理使用装饰器,我们可以写出更加优雅、可维护的Python代码。从简单的日志记录到复杂的权限系统,装饰器都能大显身手。掌握装饰器是成为Python高级程序员的重要一步。