Python闭包装饰器笔记
Python三大器有迭代器,生成器,装饰器,这三个中使用最多,最重要的就是装饰器。本篇将重要从函数嵌套开始讲起,从而引入闭包,装饰器的各种用法等。
python中的一切都是一个对象(函数也是)
1.首先让我们来了解python中的函数嵌套
1.1
# -*- coding:utf-8 -*- def outter(): print("outter()函数里面") def inner1(): return "现在在inner1()函数里面" def inner2(): return "现在在inner2()里面" print (inner1()) print (inner2()) print ("outter执行结束") # outter() # outter()函数里面 # 现在在inner1()函数里面 # 现在在inner2()里面 # outter执行结束
如果像上面写的inner1和inner2将没有什么意义,没有必要在另一个函数中执行一个函数,因为完全可以用过程化实现,所以看下面
1.2.从函数内返回函数
# -*- coding:utf-8 -*- def outter(name="inner1"): print("outter()函数里面") def inner1(): return "现在在inner1()函数里面" def inner2(): return "现在在inner2()里面" if name == "inner1": return inner1 else: return inner2 inner = outter() print(inner()) # outter()函数里面 # 现在在inner1()函数里面
1.3.函数作为参数
# -*- coding:utf-8 -*- def inner(): return "现在在inner()函数里面" def outter(func): print("outter()函数里面") print(func()) outter(inner) # outter()函数里面 # 现在在inner()函数里面
2.闭包:在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,并返回这个函数,那么将这个函数以及用到的一些变量称之为闭包。
2.1
def outter(num): def inner(a): print(a + num) return inner fun = outter(100) # fun == inner fun(1) # 101 fun2 = outter(200) fun2(1) # 201 fun(2) # 102
2.2闭包的实际用例,控制一条直线:y=ax+b,确定直线的斜率后,改变x的值获得不同的y值。
def line_slope(a, b): def line(x): return a * x + b return line line1 = line_slope(1, 1) line2 = line_slope(4, 5) print(line1(2)) print(line1(-1)) print(line2(-3)) print(line2(6)) # 3 # 0 # -7 # 29 # 这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候, # 我们通过line_slope的参数a,b说明了这两个变量的取值,这样, # 我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。 # 我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此, # 我们可以看到,闭包也具有提高代码可复用性的作用。 # 如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。 # 这样,我们就需要更多的参数传递,也减少了代码的可移植性。 # 1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成 # 2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
2.3修改闭包变量。说明:Python3通过nonlocal关键字修改闭包变量,Python2通过list来修改
python3中
def outter(start=0): def inner(): nonlocal start start += 1 return start return inner o1 = outter(5) print(o1()) print(o1()) o2 = outter(20) print(o2()) print(o2()) print(o1()) print(o1()) print(o2()) print(o2()) # 6 # 7 # 21 # 22 # 8 # 9 # 23 # 24
python2中
def outter(start=0): list1 = [start] def inner(): nonlocal start list1[0] += 1 return list1[0] return inner o1 = outter(5) print(o1()) print(o1()) o2 = outter(20) print(o2()) print(o2()) print(o1()) print(o1()) print(o2()) print(o2()) # 6 # 7 # 21 # 22 # 8 # 9 # 23 # 24
2.4LEGB 规则
# locals -> enclosing function -> globals -> builtins a = 1 # 全局变量 globals def outter(): a = 2 # 闭包变量 enclosing def inner(): a = 3 # 局部变量 locals print("a=%d" % a) return inner o = outter() o() # a=3 # locals,当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量 # enclosing,外部嵌套函数的命名空间(闭包中常见) # globals,全局变量,函数定义所在模块的命名空间 # builtins,内建模块的命名空间。 # 在Python中,有一个内建模块,该模块中有一些常用函数;在Python启动后, # 且没有执行程序员所写的任何代码前,Python会首先加载该内建模块到内存。 # 另外,该内建模块中的功能可以直接使用,不用在其前添加内建模块前缀, # 其原因是对函数、变量、类等标识符的查找是按LEGB法则,其中B即代表内建模块
3.装饰器
3.1被装饰的函数无参数
import time # 定义装饰器 def log(fun): def inner(): print(time.time()) fun() print(time.time()) return inner # 使用装饰器 @加函数名 是一种语法糖,即简写 # 真正的执行过程:send_mail = log(send_mail) => send_mail = inner @log def send_mail(): print("发送邮件") send_mail() # 1503492456.2909923 # 发送邮件 # 1503492456.2909923
3.2被装饰的函数有参数
def outter(fun): def inner(a, b): fun(a, b) return inner @outter def add(a, b): print("a+b:%d" % (a + b)) add(1, 2) add(3, 4) # a+b:3 # a+b:7
3.3被装饰的函数有不定长参数。
说明:把装饰器做成能装饰任何函数的装饰器,所以对于装饰器来说被装饰的函数的参数是不定长的。这样就应该用*args和**kwargs来当参数。
def outter(fun): def inner(*args, **kwargs): fun(*args, **kwargs) return inner @outter def add(a, b, c): print("a+b+c:%d" % (a + b + c)) t = (1, 2, 3) add(*t) # a+b+c:6 d = {"a": 1, "b": 2, "c": 10} add(**d) # a+b+c:13
3.4被装饰的函数有返回值。说明:一般情况下为了让装饰器更通用,可以有return。
def outter(fun): def inner(*args, **kwargs): temp = fun(*args, **kwargs) return temp return inner @outter def add(a, b, c): return a + b + c t = (1, 2, 3) ret = add(*t) print(ret) # 6 ret = d = {"a": 1, "b": 2, "c": 10} print(add(**d)) # 13
3.5装饰器带参数,在原有装饰器的基础上,设置外部变量。说明:装饰器带参数的执行过程是:首先将@ 和 arg_outter(arg)分离,将arg_outter(arg)的执行结果和@结合。
def arg_outter(arg): def outter(fun): def inner(): print("装饰器的参数是:%s" % arg) if arg: fun() print("我是True,我会唱歌") else: fun() return inner return outter @arg_outter(True) def song(): print("song") @arg_outter(False) def listen(): print("listen") song() listen()
3.6多层装饰器的使用。说明:多层装饰器,先看第一层的装饰器,把第一层当做函数调用,函数的参数是下一层的执行结果。
具体分析:当遇到@tag1时,发现它是一个装饰器,所以开始函数调用,即装饰谁就传谁,但是发现装饰的不是一个函数,而是一个函数外面加了一个装饰
器,意味着把tag2以及下面作为一个整体形成的结果当做参数当做tag1的参数。返回结果即第一层里面的inner函数名字。用最终装饰器的函数名字进行
接收。即link_a。所以调用link_a的时候就是调用的tag1里面的inner函数。
使用装饰器的顺序很重要,如果未按预定的顺序执行,可以更改装饰功能的整体行为。
def tag1(fun): def inner(): print("<li>", end="") fun() print("</li>", end="") return inner def tag2(f): def inner(): print("<a>", end="") f() print("</a>", end="") return inner # 多层装饰器的使用 @tag1 # link_a = tag1( tag2(link_a) ) @tag2 def link_a(): print("我是一个超链接", end="") link_a() # tag1->inner() # <li><a>我是一个超链接</a></li>
3.7类装饰器(不常用)。说明:装饰器,log(say_hello),相当于一个类构造一个对象,所以把log构造成类,对象()即调用__call__()方法.
import time class log(object): def __init__(self, fun): self.fun = fun def __call__(self): """当把对象当作函数进行调用时,被调用""" print(time.time()) self.fun() print(time.time()) @log def say_hello(): print("say_hello") say_hello() # 1503495467.9418454 # say_hello # 1503495467.9418454
4.装饰器的主要作用:
引入日志
函数执行时间统计
执行函数前预备处理
执行函数后清理功能
权限校验等场景缓存