闭包和装饰器

闭包

在函数内部再定义⼀个函数,并且这个内部函数⽤到了外部的变量,这个函数以及⽤到外部函数的变量及参数叫闭包

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('张三','李四')

你可能感兴趣的:(python,开发语言,装饰器模式,django,正则表达式)