装饰器的本质 装饰器本质上就是高阶函数的"语法糖"。你想啊,装饰器做的事情就是:接收一个函数,包一层,返回一个新的函数。这不就是典型的高阶函数吗?
@decorator 等价于 func = decorator(func),就是把原函数传给装饰器,装饰器决定什么时候调用它、传什么参数进去,然后再返回一个"升级版"的函数。
装饰器的基本结构 让我们来看一个最简单的装饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def simple_decorator (func ): def wrapper (*args, **kwargs ): print ("Before function call" ) result = func(*args, **kwargs) print ("After function call" ) return result return wrapper @simple_decorator def say_hello (name ): print (f"Hello, {name} !" ) say_hello("Alice" )
输出:
1 2 3 Before function call Hello, Alice! After function call
装饰器的工作原理
定义装饰器 :simple_decorator 是一个高阶函数,它接收一个函数 func 作为参数。
定义内部函数 :wrapper 函数是一个闭包,它可以访问外部函数的变量 func。
返回内部函数 :simple_decorator 返回 wrapper 函数。
应用装饰器 :@simple_decorator 语法将 say_hello 函数传递给 simple_decorator,然后将返回的 wrapper 函数赋值给 say_hello。
调用函数 :当我们调用 say_hello("Alice") 时,实际上是在调用 wrapper("Alice")。
执行包装逻辑 :wrapper 函数先执行一些前置逻辑,然后调用原始的 func 函数,最后执行一些后置逻辑。
装饰器的参数传递 装饰器可以接收参数,这使得装饰器更加灵活。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def decorator_with_args (prefix ): def decorator (func ): def wrapper (*args, **kwargs ): print (f"{prefix} : Before function call" ) result = func(*args, **kwargs) print (f"{prefix} : After function call" ) return result return wrapper return decorator @decorator_with_args("DEBUG" ) def say_hello (name ): print (f"Hello, {name} !" ) say_hello("Alice" )
输出:
1 2 3 DEBUG: Before function call Hello, Alice! DEBUG: After function call
装饰器的应用场景 1. 日志记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def log_decorator (func ): def wrapper (*args, **kwargs ): import datetime print (f"[{datetime.datetime.now()} ] Calling {func.__name__} " ) result = func(*args, **kwargs) print (f"[{datetime.datetime.now()} ] {func.__name__} returned {result} " ) return result return wrapper @log_decorator def add (a, b ): return a + b result = add(3 , 5 ) print (f"Result: {result} " )
2. 性能测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def timer_decorator (func ): def wrapper (*args, **kwargs ): import time start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print (f"{func.__name__} took {end_time - start_time:.4 f} seconds" ) return result return wrapper @timer_decorator def slow_function (): import time time.sleep(1 ) return "Done" result = slow_function() print (f"Result: {result} " )
3. 权限验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def require_admin (func ): def wrapper (*args, **kwargs ): current_user = {"name" : "Alice" , "is_admin" : True } if not current_user.get("is_admin" , False ): raise PermissionError("Admin permission required" ) return func(*args, **kwargs) return wrapper @require_admin def delete_user (user_id ): print (f"Deleting user {user_id} " ) return True try : result = delete_user(123 ) print (f"Result: {result} " ) except PermissionError as e: print (f"Error: {e} " )
装饰器的注意事项 1. 函数名称和文档字符串 当我们使用装饰器时,原始函数的名称和文档字符串会被包装函数覆盖。为了解决这个问题,我们可以使用 functools.wraps 装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import functoolsdef decorator (func ): @functools.wraps(func ) def wrapper (*args, **kwargs ): """Wrapper function""" print ("Before function call" ) result = func(*args, **kwargs) print ("After function call" ) return result return wrapper @decorator def say_hello (name ): """Say hello to someone""" print (f"Hello, {name} !" ) print (f"Function name: {say_hello.__name__} " )print (f"Function doc: {say_hello.__doc__} " )
2. 装饰器的顺序 当多个装饰器应用于同一个函数时,它们的执行顺序是从下到上的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def decorator1 (func ): def wrapper (*args, **kwargs ): print ("Decorator 1: Before" ) result = func(*args, **kwargs) print ("Decorator 1: After" ) return result return wrapper def decorator2 (func ): def wrapper (*args, **kwargs ): print ("Decorator 2: Before" ) result = func(*args, **kwargs) print ("Decorator 2: After" ) return result return wrapper @decorator1 @decorator2 def say_hello (name ): print (f"Hello, {name} !" ) say_hello("Alice" )
输出:
1 2 3 4 5 Decorator 1: Before Decorator 2: Before Hello, Alice! Decorator 2: After Decorator 1: After
总结 装饰器是Python中一种非常强大的特性,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。装饰器本质上是高阶函数的语法糖,它接收一个函数,返回一个新的函数。
通过本文的介绍,你应该已经对装饰器的本质和工作原理有了更深入的理解。现在,你可以尝试在自己的代码中使用装饰器,写出更加简洁、优雅的Python代码。