目录
1.闭包 <a id='01'></a>
当一个函数 inner 能够使用 outer函数的变量时,称inner为闭包:
1# 演示闭包 2def outer(): 3 i = 4 4 5 def inner(): 6 sum = i + 1 7 print(sum) 8 9 return inner # 注意这里是函数名(指代函数地址);不是inner(),inner()表示函数调用。 10 11 12f1 = outer() 13print(f1) # 此时 f1 指代函数地址,还未运行 14# print(f1()) 15f1() # 调用运行f1函数,等同于 outer()()
运行结果
闭包特点:
- 函数outer() 内部再嵌套函数inner();
- 内部函数inner() 使用了外部函数outer() 的变量 i;
- 外部函数outer() 返回内部函数的函数名inner。
2.nonlocal <a id='02'></a>
nonlocal是关键字,声明了非局部(non-local)也非全局(non-global)的变量,例如闭包函数inner外的函数;变量跟着函数存在而存在,跟着函数销毁而销毁。 它允许我们在inner函数内部 修改outer函数的i变量:
1def outer(): 2 i = 4 3 def inner(): 4 nonlocal i # 声明变量i的作用域 5 sum = i + 1 6 i += 1 7 print(sum) 8 return inner 9f1 = outer() 10for i in range(5): 11 # 使用for循环运行5次 12 f1()
运行结果:
可以看出5次运行,每次i的值都加了1;直到结束。而把 nonlocal i注释掉会报错,在注释掉i+=1则不会报错:

3.装饰器 <a id='03'></a>
当一个函数 inner 能够使用 outer函数的变量,当这个变量是一个函数时,这个闭包就是一个装饰器。 在定义一个类时,使用@classmethod和@staticmethod修饰方法,这样的写法就是闭包的语法糖。
1# 1.创建一个统计函数运行次数的装饰器 2def func_count(func): 3 a = 0 # 函数初始值 4 def inner(str):# 传入函数及其参数 5 nonlocal a # 在内部函数修改外部函数的变量 6 a += 1 7 func(str) # 运行传进来的函数 8 print(f'执行了{a}次!') # 函数每运行一次就提示一次 9 return inner 10 11# 2.定义输出函数并使用装饰器 12@func_count 13def f1(str): 14 print(str) 15 16# 3.执行函数 17for i in range(5): 18 f1('hello world')
运行结果:
可以看出装饰器的特点与闭包差不多:
- 有函数嵌套;
- 内部函数使用了外部函数的变量,这个变量可以是函数;
- 外部函数返回内部函数的函数名
- 为原来的f1函数增加了新的功能:可以查看执行次数。
多个装饰器
一个函数可用多个装饰器来装饰,在定义一个装饰器;
1# 创建统计函数执行时间的装饰器 2import time 3def func_time(func): 4 time_count = 0 5 def inner(str): 6 nonlocal time_count # 在内部函数修改外部函数的变量 7 time_start = time.time() #记录开始时间 8 func(str) 9 time_count += time.time() - time_start #新的总运行时间=旧的总运行时间+本次执行时间 10 print(f'总执行用时{time_count}') 11 return inner
在原来的基础上改造 f1函数:
1@func_time 2@func_count 3def f1(str): 4 a = 0 5 for i in range(100000): 6 a += 1 # 模拟函数运行时间 7 print(f'{str}-{a}')
同上运行5次 f1函数,运行结果:

带有参数的装饰器
在为原来函数添加装饰器时,可对装饰器传入参数;具体实现就是三层嵌套的函数:
1# 定义带有参数的装饰器 2def parameter(n): # 传入装饰器参数 3 def outer(func): 4 def inner(*args, **kwargs): # 传入可变参数或不定长参数,*元组类型参数(1,2,3);**字典类型参数(a=1,a=2,a=3) 5 for i in range(n): 6 result = func(*args, **kwargs) 7 return result # 如果func没有返回值可以省略不写 8 return inner 9 return outer 10 11@parameter(5) 12def f1(str): 13 print(f"Hello {str}!") 14 15f1("World")
运行结果:
