目录
- 一、基本原理
- 二、核心作用
- 三、Demo示例
- 示例1:基本装饰器
- 示例2:带参数的装饰器
- 示例3:多个装饰器
- 四、正确使用装饰器
- 五、pyparamvalidate 装饰器项目
装饰器(Decorators)是 Python 中一种强大而灵活的功能,它允许你在不修改原始函数代码的情况下,动态地修改或增强函数的行为。
一、基本原理
装饰器本质上是一个函数,它接受一个函数作为输入,并返回一个新的函数。在这个过程中,可以在新函数中添加额外的功能,或者修改原始函数的行为。
二、核心作用
- 代码重用: 装饰器可以用于包装通用的功能,使其能够被多个函数共享。
- 代码增强: 通过装饰器在不改变原始函数结构的情况下,为其增加额外的功能。
- 面向切面编程: 装饰器允许在不修改函数代码的情况下,将横切关注点(如日志、性能监控)从业务逻辑中分离出来。
三、Demo示例
示例1:基本装饰器
def simple_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@simple_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.
@decorator
语法糖是 Python 装饰器的简便写法,这段代码等效于:say_hello = simple_decorator(say_hello)
。
使用 @decorator
语法糖时,解释器会自动将下方的函数传递给装饰器,并将返回的结果重新赋值给原函数名。这样可以在不改变原函数调用方式的情况下应用装饰器。
示例2:带参数的装饰器
def decorator_with_args(arg):def actual_decorator(func):def wrapper(*args, **kwargs):print(f"Decorator argument: {arg}")for i in range(arg):func(*args, **kwargs)return wrapperreturn actual_decorator@decorator_with_args(3)
def greet(name):print(f"Hello, {name}!")greet("Alice")
输出结果:
Decorator argument: 3
Hello, Alice!
Hello, Alice!
Hello, Alice!
示例3:多个装饰器
def decorator1(func):def wrapper1():print("Decorator 1")func()return wrapper1def decorator2(func):def wrapper2():print("Decorator 2")func()return wrapper2@decorator1
@decorator2
def my_function():print("Original function")my_function()
输出结果:
Decorator 1
Decorator 2
Original function
执行顺序说明:
@decorator2
在my_function
上应用了decorator2
装饰器。实际上,这相当于执行了my_function = decorator2(my_function)
,将my_function
替换为decorator2
返回的新函数wrapper2
。- 然后,
@decorator1
在已经被decorator2
装饰过的my_function
上再次应用了decorator1
装饰器。这相当于执行了my_function = decorator1(decorator2(my_function))
,将decorator2
返回的函数wrapper2
替换为decorator1
返回的新函数wrapper1
。 - 当调用
my_function()
时,实际上是调用了经过两个装饰器包装后的新函数。执行顺序是先执行最外层的decorator1
,再执行内层的decorator2
,最后执行原始函数。- 执行
decorator1
,即执行wrapper1()
,"Decorator 1"被打印出来,然后执行func()
; - 此时
func
为wrapper2
,func()
相当于执行wrapper2()
, “Decorator 2” 被打印出来,然后执行func()
; - 此时
func
为my_function
,func()
相当于执行my_function()
,"Original function"被打印出来,结束。
- 执行
四、正确使用装饰器
下面是一段问题代码:
def log_decorator(func):def wrapper(*args, **kwargs):print(f"Calling {func.__name__} with arguments {args} and keyword arguments {kwargs}")result = func(*args, **kwargs)print(f"{func.__name__} returned: {result}")return resultreturn wrapper@log_decorator
def add(a, b):return a + b@log_decorator
def multiply(x, y):return x * yadd_result = add(2, 3)
print(f"add function name: {add.__name__}") # 输出结果:add function name: wrapperprint(f"===================================")multiply_result = multiply(4, 5)
print(f"multiply function name: {multiply.__name__}") # 输出结果:multiply function name: wrapper
输出结果:
Calling add with arguments (2, 3) and keyword arguments {}
add returned: 5
add function name: wrapper
===================================
Calling multiply with arguments (4, 5) and keyword arguments {}
multiply returned: 20
multiply function name: wrapper
从输出结果中,可以看出,add.__name__
和 multiply.__name__
均没有正确的显示函数名称。
为了解决这个问题,python 提供了内置的装饰器 @functools.wraps
,它的作用是在自定义装饰器中,复制原始函数的元信息到装饰器返回的新函数中,从而保留原函数的属性,包括:函数名称、文档字符串、参数信息等。
下面是正确使用装饰器的代码:
def log_decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):print(f"Calling {func.__name__} with arguments {args} and keyword arguments {kwargs}")result = func(*args, **kwargs)print(f"{func.__name__} returned: {result}")return resultreturn wrapper@log_decorator
def add(a, b):return a + b@log_decorator
def multiply(x, y):return x * yadd_result = add(2, 3)
print(f"add function name: {add.__name__}") # 输出结果:add function name: addprint(f"===================================")multiply_result = multiply(4, 5)
print(f"multiply function name: {multiply.__name__}") # 输出结果:multiply function name: multiply
输出结果:
Calling add with arguments (2, 3) and keyword arguments {}
add returned: 5
add function name: add
===================================
Calling multiply with arguments (4, 5) and keyword arguments {}
multiply returned: 20
multiply function name: multiply
五、pyparamvalidate 装饰器项目
pyparamvalidate
是一个简单易用的函数参数验证器。它提供了各种内置验证器,支持自定义验证规则,有助于 python 开发人员轻松进行函数参数验证,提高代码的健壮性和可维护性。
项目地址:github 。如果该项目对您有帮助,麻烦帮忙在 github
上点个小星星。
如果您想进一步深入了解装饰器的使用,请参考实现过程:pyparamvalidate 参数校验器,从编码到发布全过程 。
该项目最终的实现效果是一个ParameterValidator
装饰器类,如下:
@ParameterValidator("description").is_string().is_not_empty()
@ParameterValidator("gender", "Invalid").is_allowed_value(["male", "female"], "Gender must be either 'male' or 'female'")
@ParameterValidator("age", param_rule_des="Age must be a positive number").is_int().is_positive()
@ParameterValidator("name").is_string(exception_msg="Name must be a string").is_not_empty()
def example_function(name, age, gender='male', **kwargs):description = kwargs.get("description")return name, age, gender, description