装饰器(Decorator)是Python中一种强大的语法特性,它允许在不修改原函数代码的情况下,为函数添加额外的功能。
目录
6.2 @classmethod 和 @staticmethod
1. 装饰器定义
装饰器本质上是一个可调用的Python对象(通常是函数或类),它接受一个函数作为输入并返回一个新的函数。装饰器的主要目的是在不改变原函数定义的情况下,扩展函数的行为。
装饰器的语法使用 @decorator_name 来应用在函数或方法上。
Python 还提供了一些内置的装饰器,比如 @staticmethod 和 @classmethod,用于定义静态方法和类方法。
装饰器的应用场景:
- 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
- 性能分析: 可以使用装饰器来测量函数的执行时间。
- 权限控制: 装饰器可用于限制对某些函数的访问权限。
- 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。
2. 装饰器基本语法
Python 装饰允许在不修改原有函数代码的基础上,动态地增加或修改函数的功能,装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象。
基本装饰器语法使用@
符号:
@decorator
def function():
pass
#这等同于:
def function():
pass
function = decorator(function)
3. 使用装饰器
3.1 简单装饰器示例
def simple_decorator(func):
def wrapper():
print("Before function call")
func()
print("After function call")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
say_hello()
这段代码演示了 Python 中装饰器的基本用法。装饰器是一种在不修改原函数代码的情况下,为函数添加额外功能的方式。
1. 定义装饰器函数 simple_decorator
:
-
simple_decorator
是一个装饰器函数,它接收一个函数func
作为参数 -
内部定义了
wrapper
函数,它会在调用原函数前后打印信息 -
最后返回
wrapper
函数
2. 应用装饰器:
-
@simple_decorator
语法是将simple_decorator
装饰器应用到say_hello
函数上 -
这等同于:
say_hello = simple_decorator(say_hello)
3. 调用被装饰的函数:
-
实际调用的是
wrapper
函数
4. 执行流程:
-
当 Python 解释器遇到
@simple_decorator
时,它会立即装饰下面的函数 -
say_hello
函数被传递给simple_decorator
作为参数 -
simple_decorator
返回wrapper
函数,替换原来的say_hello
-
当调用
say_hello()
时,实际上调用的是wrapper()
函数 -
wrapper()
执行其内部代码,包括调用原始say_hello
函数
3.2 保留原函数信息
使用functools.wraps
保留原函数的元信息:
from functools import wraps
def preserving_decorator(func):
@wraps(func) # 使用 wraps 保留 func 的元信息
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@preserving_decorator
def greet(name):
"""Display a greeting message"""
print(f"Hello, {name}!")
print(greet.__name__) # 输出: greet
print(greet.__doc__) # 输出: Display a greeting message
print("---------")
print(greet("KKK"))
这段代码演示了如何使用 functools.wraps
来保留被装饰函数的元信息(如函数名、文档字符串等)。
1. 导入 wraps 工具:
from functools import wraps
-
wraps
是一个装饰器工厂函数,用于保留被装饰函数的元信息
2. 定义装饰器函数 preserving_decorator
:
def preserving_decorator(func):
@wraps(func) # 使用 wraps 保留 func 的元信息
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
-
装饰器内部使用
@wraps(func)
装饰wrapper
函数 -
*args, **kwargs
使装饰器可以接受任意参数 -
打印被调用函数名后执行原函数
3. 应用装饰器:
@preserving_decorator
def greet(name):
"""Display a greeting message"""
print(f"Hello, {name}!")
4. 验证元信息保留:
print(greet.__name__) # 输出: greet
print(greet.__doc__) # 输出: Display a greeting message
5. 关键点说明:
-
@wraps(func)
的作用:-
将原函数 (
func
) 的元信息(__name__
,__doc__
,__module__
等)复制到包装函数 (wrapper
) 中 -
如果不使用
wraps
,greet.__name__
会返回wrapper
,greet.__doc__
会返回None
-
-
带参数的装饰器:
-
wrapper(*args, **kwargs)
使装饰器能够处理任何参数的函数 -
return func(*args, **kwargs)
确保原函数的返回值被正确传递
-
- 实际调用流程:
-
先执行
wrapper("Alice")
-
打印 "Calling greet"
-
执行原
greet("Alice")
函数 -
返回原函数的返回值(本例中为 None)
-
4. 对比示例:
如果不使用 @wraps
:
def bad_decorator(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@bad_decorator
def greet(name):
"""Display a greeting message"""
print(f"Hello, {name}!")
print(greet.__name__) # 输出: wrapper
print(greet.__doc__) # 输出: None
4. 带参数的装饰器
装饰器本身也可以接受参数:
from functools import wraps
def repeat(num_times): # 外层函数接收装饰器参数
def decorator(func): # 中间函数接收被装饰的函数
@wraps(func) # 保留原函数元信息
def wrapper(*args, **kwargs): # 内层包装函数
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(num_times=3) # 使用装饰器并传入参数
def greet(name):
print(f"Hello {name}")
greet("Alice")
这段代码演示了一个可以接受参数的装饰器 repeat
,它会让被装饰的函数重复执行指定的次数。
代码解析
from functools import wraps
def repeat(num_times): # 外层函数接收装饰器参数
def decorator(func): # 中间函数接收被装饰的函数
@wraps(func) # 保留原函数元信息
def wrapper(*args, **kwargs): # 内层包装函数
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
这是一个三层嵌套结构:
-
最外层
repeat(num_times)
接收装饰器参数 -
中间层
decorator(func)
接收被装饰的函数 -
最内层
wrapper(*args, **kwargs)
实现装饰逻辑
@repeat(num_times=3) # 使用装饰器并传入参数
def greet(name):
print(f"Hello {name}")
#这相当于:
greet = repeat(num_times=3)(greet)
执行流程
当调用 greet("Alice")
时:
-
首先执行
repeat(num_times=3)
,返回decorator
函数 -
然后
decorator(greet)
被调用,返回wrapper
函数 -
最后调用
wrapper("Alice")
,它会:-
循环执行
greet("Alice")
3次 -
返回最后一次执行的结果
-
5. 类装饰器
除了函数装饰器,Python 还支持类装饰器。
类装饰器(Class Decorator)是一种用于动态修改类行为的装饰器,它接收一个类作为参数,并返回一个新的类或修改后的类。
类装饰器可以用于:
-
添加/修改类的方法或属性
-
拦截实例化过程
-
实现单例模式、日志记录、权限检查等功能
类装饰器有两种常见形式:
-
函数形式的类装饰器(接收类作为参数,返回新类)
-
类形式的类装饰器(实现 __call__ 方法,使其可调用)
5.1 函数形式的类装饰器
def class_decorator(cls): # 类装饰器函数,接收一个类作为参数
class Wrapped(cls): # 创建一个继承自原类的新类
def new_method(self):
print("This is a new method added by decorator")
def existing_method(self):
print("Method is being modified by decorator")
super().existing_method() # 调用父类(原类)的方法
return Wrapped # 返回修改后的新类
@class_decorator # 应用类装饰器
class MyClass:
def existing_method(self):
print("Original method")
obj = MyClass()
print("-----------")
obj.existing_method() # 被装饰器修改的方法
print("-----------")
obj.new_method() # 装饰器添加的新方法
print("-----------")
obj.existing_method() # 被装饰器修改的方法
print("-----------")
关键点说明:
-
类装饰器原理:
-
接收一个类作为参数
-
创建一个继承自原类的新类
Wrapped
-
在新类中添加或修改方法
-
返回修改后的新类
-
-
方法覆盖与扩展:
-
new_method()
:完全新增的方法 -
existing_method()
:修改原有方法,通过super()
保留原功能
-
-
继承关系:
Wrapped -> cls (原类) -> object
-
元信息保留:
-
类名仍然是
MyClass
(因为Wrapped
是内部类名) -
原类的其他属性和方法保持不变
-
5.2 类形式的类装饰器
class ClassDecorator:
def __init__(self, func): # 初始化方法,接收被装饰的函数
self.func = func # 保存函数为实例属性
def __call__(self, *args, **kwargs): # 使实例可调用
print("Before function call")
result = self.func(*args, **kwargs) # 调用原函数
print("After function call")
return result # 返回原函数的结果
@ClassDecorator
def say_hello():
print("Hello!")
say_hello()
这段代码展示了如何使用类来实现装饰器功能。与函数装饰器不同,类装饰器利用了类的 __init__
和 __call__
方法来实现装饰逻辑。
执行流程:
-
当解释器遇到
@ClassDecorator
时:-
创建
ClassDecorator
实例,调用__init__
方法 -
将被装饰函数
say_hello
传递给func
参数保存
-
-
当调用
say_hello()
时:-
实际上是调用
ClassDecorator
实例的__call__
方法 -
执行
__call__
方法中的包装逻辑 -
最终调用原始
say_hello
函数
-
关键点说明:
-
类装饰器工作原理:
-
__init__
:在装饰时调用,接收被装饰函数 -
__call__
:在被装饰函数调用时执行,实现装饰逻辑
-
-
与函数装饰器的区别:
-
函数装饰器使用嵌套函数
-
类装饰器使用类方法和实例状态
-
-
优势:
-
可以维护状态(通过实例属性)
-
结构更清晰,适合复杂装饰逻辑
-
可以通过继承扩展功能
-
扩展示例:
带参数的类装饰器:
class ParametrizedDecorator:
def __init__(self, prefix):
self.prefix = prefix
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f"{self.prefix} - Before call")
result = func(*args, **kwargs)
print(f"{self.prefix} - After call")
return result
return wrapper
@ParametrizedDecorator("DEBUG")
def say_hi():
print("Hi!")
say_hi()
6. 内置装饰器
Python提供了一些有用的内置装饰器:
-
@staticmethod
: 将方法定义为静态方法,不需要实例化类即可调用。 -
@classmethod
: 将方法定义为类方法,第一个参数是类本身(通常命名为cls
)。 -
@property
: 将方法转换为属性,使其可以像属性一样访问。
6.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:
self._radius = value
else:
raise ValueError("Radius must be positive")
c = Circle(5) # 初始化半径为5
print(c.radius) # 通过property获取值 → 5
c.radius = 10 # 通过setter设置值
print(c.radius) # 10
c.radius = -5 # 触发ValueError异常
关键组件说明
-
@property
装饰器:-
将方法转换为只读属性
-
当访问
instance.radius
时自动调用此方法
-
-
Setter 方法:
-
使用
@属性名.setter
装饰器定义 -
当赋值
instance.radius = value
时自动调用 -
可以添加验证逻辑
-
-
命名约定:
-
_radius
:单下划线表示受保护属性(约定上的私有) -
公共接口使用无下划线名称
-
设计优势
-
封装性:
-
隐藏内部实现细节(使用
_radius
存储实际值) -
对外提供统一的访问接口
-
-
数据验证:
-
确保半径不会设置为负数
-
在赋值时立即捕获非法输入
-
-
兼容性:
-
可以随时修改内部实现而不影响外部代码
-
例如未来可以改为存储直径而非半径
-
6.2 @classmethod 和 @staticmethod
class MyClass:
class_var = "Class variable"
def __init__(self, instance_var):
self.instance_var = instance_var
@classmethod
def class_method(cls):
print(f"Accessing {cls.class_var} from class method")
@staticmethod
def static_method():
print("This is a static method")
MyClass.class_method() # 不需要实例
MyClass.static_method() # 不需要实例
关键概念说明
1. 类方法 (@classmethod
)
-
特点:
-
第一个参数是
cls
(类对象本身) -
可以访问类变量和其他类方法
-
不能访问实例变量(因为不需要实例就能调用)
-
2. 静态方法 (@staticmethod
)
-
特点:
-
不需要
self
或cls
参数 -
不能访问类变量或实例变量
-
类似于普通函数,只是属于类的命名空间
-
7. 多个装饰器的堆叠
装饰器可以堆叠使用,执行顺序是从下往上:
def decorator1(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Decorator 1 - Before")
result = func(*args, **kwargs)
print("Decorator 1 - After")
return result
return wrapper
def decorator2(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Decorator 2 - Before")
result = func(*args, **kwargs)
print("Decorator 2 - After")
return result
return wrapper
@decorator1
@decorator2
def my_function():
print("Original function")
my_function()
装饰器的应用顺序是从下往上的,即:
-
先应用
@decorator2
-
然后应用
@decorator1
函数调用
当调用 my_function()
时,执行顺序如下:
-
执行
decorator1
的wrapper
前半部分 - 打印 "Decorator 1 - Before" -
执行
decorator2
的wrapper
前半部分 - 打印 "Decorator 2 - Before" -
执行原始函数
my_function
- 打印 "Original function" -
执行
decorator2
的wrapper
后半部分 - 打印 "Decorator 2 - After" -
执行
decorator1
的wrapper
后半部分 - 打印 "Decorator 1 - After"
关键点
-
装饰器是从下往上应用的
-
执行顺序是"由外向内"再"由内向外"
-
@wraps(func)
保留了原始函数的元信息 -
装饰器可以叠加使用,形成装饰器链
这种装饰器模式常用于添加日志、权限检查、性能测试等横切关注点功能。
8. 装饰器的应用
装饰器在实际开发中有广泛的应用:
8.1 计时装饰器
import time
from functools import wraps
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 slow_function():
time.sleep(2)
slow_function()
8.2 缓存装饰器
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)) # 快速计算,因为有缓存
8.3 权限检查装饰器
from functools import wraps
def requires_admin(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.is_admin:
return func(user, *args, **kwargs)
else:
raise PermissionError("Admin privileges required")
return wrapper
class User:
def __init__(self, name, is_admin):
self.name = name
self.is_admin = is_admin
@requires_admin
def delete_database(user):
print(f"Database deleted by {user.name}")
admin = User("Admin", True)
regular = User("Regular", False)
delete_database(admin) # 正常工作
delete_database(regular) # 抛出PermissionError
8.4 日志记录装饰器
from functools import wraps
def log_activity(func):
@wraps(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_activity
def add_numbers(a, b):
return a + b
add_numbers(3, 5)
装饰器是Python中非常强大的工具,合理使用可以使代码更加简洁、可读和可维护。