在函数内部再定义⼀个函数,并且这个内部函数⽤到了外部的变量,这个函数以及⽤到外部函数的变量及参数叫闭包
def fun_a(num_a):
# 在函数内部再定义⼀个函数,并且这个内部函数⽤到了外部的变量
def fun_b(num_b):
print("in test_in 函数, number_in is %d" % num_b)
return num_a + num_b
# 这⾥返回的就是闭包的结果
return fun_b
# 给fun_a函数赋值,这个10就是给参数num
ret = fun_a(10)
# 注意这⾥的10其实给参数fun_b
print(ret(10))
再比如二次函数:
def line_conf(a, b):
def line(x):
return a * x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5)) # 结果 6
print(line2(5)) # 结果 25
从这段代码中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。
我们只需要变换参数a,b,就可以获得不同的直线表达函数。
因此,闭包也具有提⾼代码可复⽤性的作⽤。如果没有闭包,我们需要每次创建函
数的时候同时说明a,b,x。
def fun_a():
fun_list = []
for i in range(1, 4):
def fun_b():
return i * i
fun_list.append(fun_b)
return fun_list
f1, f2, f3 = fun_a()
print(f1(), f2(), f3())
这段代码中,我们想得到是1,4,9,可是执行结果却是9,9,9
这是因为,返回的函数引用了变量 i ,但不是立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3
最简单的方法就是改为def fun_b(_i=i),这样就每次都把i值传入了
装饰器的作用就是为已经存在的对象添加额外的功能
def test1(func):
def test2():
print('帮你把饭做好')
func()
print('洗碗')
return test2
@test1 # 相当于eat=test1(eat)
def eat():
print('我正在吃饭')
执行结果为:
帮你把饭做好
我正在吃饭
洗碗
这样就可以不用做饭洗碗,直接吃饭了
再比如,下面我们用装饰器logger来给所有关于work的函数增加记录日志的功能
from functools import wraps
import time
def logger(func): # 定义一个记录日志的装饰器
@wraps(func)
def write_logging():
print('[info]--时间是:%s'%time.strftime('%H:%M:%S',time.localtime())) # 格式化
func()
return write_logging
@logger # 使用装饰器给所有的work增加记录日志的功能
def work(): # 这里使用装饰器的函数不能带参数
print('我在工作')
结果为:[info]--时间是:16:33:02
我在工作
@wraps(func)作用是用func函数,即原函数来封装高阶函数,因为work函数调用装饰器后函数名其实是write_logging,加上@wraps(func)后函数名还是func,相当于在原函数基础上增加功能。
上面的装饰器,在work函数中不能传参数,不能知道是谁在哪个时间工作,我们如果想给func函数增加参数,就要在write_logging处增加参数:
def logger(func): # 定义一个记录日志的装饰器
@wraps(func)
def write_logging(*args,**kwargs): # 可以传多个参数,*args,**kwargs多种参数类型
print('[info]--时间是:%s'%time.strftime('%H:%M:%S',time.localtime())) # 格式化
func(*args,**kwargs)
return write_logging
@logger
def work_2(name):
print('%s 在工作'%name)
work_2('张三')
我们也可以给装饰器本身设置参数,比如我们要把这些数据输入到哪个文件:
def main_logger(log_file='out.log'): # 不写的话就是默认值
def logger(func): # 定义一个记录日志的装饰器
@wraps(func)
def write_logging(*args,**kwargs):
print('[info]--时间是:%s'%time.strftime('%H:%M:%S',time.localtime()))
with open(log_file,'a') as f:
f.write('log'+'\n')
func(*args,**kwargs)
return write_logging
return logger
@main_logger('work2.log') # 再嵌套一层函数,这样就可以传入想写的参数
def work_2(name):
print('%s 在工作'%name)
work_2('张三')
我们在写代码时如果才想起来调用库函数,可以先写需要的函数,alt加enter快速调用库~
在我们的函数,线程进程等知识点都可以包装成一个类,再由我们自己去手动调用,这样不仅方便,还可以提高代码的可重用性。我们也可以将装饰器封装成一个类:
class Logs(object):
def __init__(self,log_file='out.log',level='info'):
self.log_file=log_file
self.level=level
def __call__(self,func): # 定义装饰器的,需要有一个接受函数
@wraps(func)
def write_logging(*args, **kwargs):
print('[%s]--时间是:%s' % (self.level,time.strftime('%H:%M:%S', time.localtime())))
with open(self.log_file, 'a') as f:
f.write('log' + '\n')
func(*args, **kwargs)
return write_logging
@Logs(log_file='work2.log',level='W') # 在__init__中设置的传入参数
def work_2(name,name2):
print('%s和%s在工作'%(name,name2))
work_2('张三','李四')