目录
1.闭包
当一个函数 inner 能够使用 outer函数的变量时,称inner为闭包:
# 演示闭包
def outer():
i = 4
def inner():
sum = i + 1
print(sum)
return inner # 注意这里是函数名(指代函数地址);不是inner(),inner()表示函数调用。
f1 = outer()
print(f1) # 此时 f1 指代函数地址,还未运行
# print(f1())
f1() # 调用运行f1函数,等同于 outer()()
运行结果 闭包特点:
- 函数outer() 内部再嵌套函数inner();
- 内部函数inner() 使用了外部函数outer() 的变量 i;
- 外部函数outer() 返回内部函数的函数名inner。
2.nonlocal
nonlocal是关键字,声明了非局部(non-local)也非全局(non-global)的变量,例如闭包函数inner外的函数;变量跟着函数存在而存在,跟着函数销毁而销毁。 它允许我们在inner函数内部 修改outer函数的i变量:
def outer():
i = 4
def inner():
nonlocal i # 声明变量i的作用域
sum = i + 1
i += 1
print(sum)
return inner
f1 = outer()
for i in range(5):
# 使用for循环运行5次
f1()
运行结果: 可以看出5次运行,每次i的值都加了1;直到结束。而把 nonlocal i注释掉会报错,在注释掉i+=1则不会报错:
3.装饰器
当一个函数 inner 能够使用 outer函数的变量,当这个变量是一个函数时,这个闭包就是一个装饰器。 在定义一个类时,使用@classmethod和@staticmethod修饰方法,这样的写法就是闭包的语法糖。
# 1.创建一个统计函数运行次数的装饰器
def func_count(func):
a = 0 # 函数初始值
def inner(str):# 传入函数及其参数
nonlocal a # 在内部函数修改外部函数的变量
a += 1
func(str) # 运行传进来的函数
print(f'执行了{a}次!') # 函数每运行一次就提示一次
return inner
# 2.定义输出函数并使用装饰器
@func_count
def f1(str):
print(str)
# 3.执行函数
for i in range(5):
f1('hello world')
运行结果: 可以看出装饰器的特点与闭包差不多:
- 有函数嵌套;
- 内部函数使用了外部函数的变量,这个变量可以是函数;
- 外部函数返回内部函数的函数名
- 为原来的f1函数增加了新的功能:可以查看执行次数。
多个装饰器
一个函数可用多个装饰器来装饰,在定义一个装饰器;
在原来的基础上改造 f1函数:# 创建统计函数执行时间的装饰器 import time def func_time(func): time_count = 0 def inner(str): nonlocal time_count # 在内部函数修改外部函数的变量 time_start = time.time() #记录开始时间 func(str) time_count += time.time() - time_start #新的总运行时间=旧的总运行时间+本次执行时间 print(f'总执行用时{time_count}') return inner
同上运行5次 f1函数,运行结果:@func_time @func_count def f1(str): a = 0 for i in range(100000): a += 1 # 模拟函数运行时间 print(f'{str}-{a}')
带有参数的装饰器
在为原来函数添加装饰器时,可对装饰器传入参数;具体实现就是三层嵌套的函数:# 定义带有参数的装饰器 def parameter(n): # 传入装饰器参数 def outer(func): def inner(*args, **kwargs): # 传入可变参数或不定长参数,*元组类型参数(1,2,3);**字典类型参数(a=1,a=2,a=3) for i in range(n): result = func(*args, **kwargs) return result # 如果func没有返回值可以省略不写 return inner return outer
@parameter(5) def f1(str): print(f"Hello {str}!")
f1("World")
``` 运行结果: