本文是《Python全栈修炼之路》系列的第14篇,属于进阶修炼篇前半部分。适合已掌握Python基础语法,希望深入理解Python核心机制的读者。前言装饰器(Decorator)是Python中最具特色的语法特性之一,它允许我们在不修改原函数代码的前提下,为函数添加额外的功能。从日志记录、性能计时到权限校验、缓存优化,装饰器无处不在。本文将从闭包概念出发,逐步深入装饰器的各种形态,揭示其底层原理,并通过实战项目展示装饰器的强大威力。一、知识点讲解1.1 闭包(Closure)—— 装饰器的基石在理解装饰器之前,必须先掌握闭包的概念。闭包是指引用了外部作用域变量的函数,即使外部函数已经执行完毕,闭包仍然可以访问这些变量。defouter_function(x):"""外部函数"""definner_function(y):"""内部函数(闭包)"""returnx+y# 引用了外部函数的变量xreturninner_function# 创建闭包closure=outer_function(10)print(closure(5))# 15print(closure(20))# 30# 查看闭包引用的外部变量print(closure.__closure__)# (cell at ...,)print(closure.__closure__[0].cell_contents)# 10闭包的核心要素:要素说明嵌套函数函数内部定义另一个函数外部变量引用内部函数引用外部函数的变量返回内部函数外部函数返回内部函数(不是调用)# 更实用的闭包示例:计数器defmake_counter():count=0defcounter():nonlocalcount# 声明使用外部非全局变量count+=1returncountreturncounter counter_a=make_counter()counter_b=make_counter()print(counter_a())# 1print(counter_a())# 2print(counter_b())# 1(独立的计数器)1.2 函数装饰器基础装饰器本质上是一个接收函数作为参数并返回函数的高阶函数。# 最简单的装饰器defmy_decorator(func):defwrapper():print("函数执行前")func()print("函数执行后")returnwrapperdefsay_hello():print("Hello!")# 手动应用装饰器say_hello=my_decorator(say_hello)say_hello()# 输出:# 函数执行前# Hello!# 函数执行后使用@语法糖:@my_decoratordefsay_hello():print("Hello!")# 等价于:say_hello = my_decorator(say_hello)带参数的被装饰函数:defmy_decorator(func):defwrapper(*args,**kwargs):# 接收任意参数print(f"调用函数:{func.__name__}")print(f"位置参数:{args}")print(f"关键字参数:{kwargs}")result=func(*args,**kwargs)print(f"返回值:{result}")returnresultreturnwrapper@my_decoratordefadd(a,b,c=0):returna+b+c add(1,2,c=3)# 输出:# 调用函数: add# 位置参数: (1, 2)# 关键字参数: {'c': 3}# 返回值: 61.3 带参数的装饰器有时装饰器本身也需要参数,这时需要三层嵌套。defrepeat(num_times):"""接收参数的装饰器工厂"""defdecorator(func):defwrapper(*args,**kwargs):for_inrange(num_times):result=func(*args,**kwargs)returnresultreturnwrapperreturndecorator@repeat(num_times=3)defgreet(name):print(f"Hello,{name}!")greet("Alice")# 输出3次: Hello, Alice!执行顺序解析:@repeat(num_times=3)defgreet(name):pass# 等价于:# decorator = repeat(num_times=3) # 先执行,返回装饰器# greet = decorator(greet) # 再执行装饰1.4 类装饰器除了函数,类也可以作为装饰器。类装饰器通过__call__方法实现。classCountCalls:"""统计函数被调用次数的装饰器类"""def__init__(self,func):self.func=func self.count=0def__call__(self,*args,**kwargs):self.count+=1print(f"{self.func.__name__}被调用了{self.count}次")returnself.func(*args,**kwargs)@CountCallsdef