闭包
- 闭包其实就是在一个函数中嵌套另一个函数的定义。
- 闭包的作用:包括了外部函数的局部变量,这些局部变量在外部函数返回后也继续存在,并能被内部函数引用。
======================程序====================
def test(number):
def test_in(number_in):
print("in test_in 函数, number_in is %d" % number_in)
return number+number_in
# 其实这里返回的就是闭包的结果
return test_in
# 给test函数赋值,这个20就是给参数number
ret = test(20)
# 注意这里的100其实给参数number_in
print(ret(100))
#注 意这里的200其实给参数number_in
print(ret(200))
====================运行结果===================
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
关于闭包的实际例子
====================程序===============
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))
print(line2(5))
====================结果===============
6
25
这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。
注意点:
由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
闭包内修改外部函数的变量
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1())
print(c1())
c2 = counter(50)
print(c2())
print(c2())
print(c1())
print(c1())
print(c2())
print(c2())
- 思考:函数、匿名函数、闭包、对象 当做实参时 有什么区别?
- 匿名函数能够完成基本的简单功能,传递是这个函数的引用 只有功能
- 普通函数能够完成较为复杂的功能,传递是这个函数的引用 只有功能
- 闭包能够将较为复杂的功能,传递是这个闭包中的函数以及数据,因此传递是功能+数据
- 对象能够完成最为复杂的功能,传递是很多数据+很多功能,因此传递是功能+数据
装饰器
在Python中函数也是一个对象,所以可以
- 将函数复制给变量
- 将函数当做参数
- 返回一个函数
它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能 装饰器的返回值也是一个函数/类对象
- 闭包主要的应用就是充当装饰器
- 形式为@<函数名>(实际上==》<函数名> = set_fun(<函数名>))
装饰器在调用函数之前,已经被python解释器执行了,所以要牢记 当调用函数之前 其实已经装饰好了,尽管调用就可以了
装饰器(decorator)功能
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
def f1():
print('---这是第一个语句验证----')
print('-----test1----')
print('---这是第二个语句验证----')
def f2():
print('---这是第一个语句验证----')
print('-----test2----')
print('---这是第二个语句验证----')
def f3():
print('---这是第一个语句验证----')
print('-----test3----')
print('---这是第二个语句验证----')
f1()
f2()
f3()
***************执行结果***************
再看另一份代码:
def set_func(func):
def call_func():
print("---这是第一个语句----")
func()
print("---这是第二个语句----")
return call_func
@set_func # 等价于test1 = set_func(test1)
def test1():
print("-----test1----")
# test1 = set_func(test1)
test1()
***************执行结果***************
---这是第一个语句----
-----test1----
---这是第二个语句----
两段代码的执行结果都是相同的,但是用了装饰器的代码 相对于比较方便.
- 代码中@set_func 等价于test1 = set_func(test1)
- 执行过程:当执行 @set_func 时,相当于把 test1 当作实参传给了 set_func()
- test1先作为参数赋值给func后, test1接收指向set_fun返回的call_func
test1()
而此时 func形参 指向了函数 test1
然后到执行 return call_func时
就是返回call_func这个引用给 test1(因为是test1调用的set_func)
也就是说这时test1 指向的就是 call_func() 函数
然后调用 test1() 时,就相当于调用 call_func()
执行到func() 时,因为它指向的是 test1 里面的语句
所以执行的是 print("-----test1----")
装饰有参数无返回值的程序
def set_func(func):
def call_func(a):
print("---这是权限验证1----")
print("---这是权限验证2----")
func(a)
return call_func
@set_func # 相当于 test1 = set_func(test1)
def test1(num):
print("-----test1----%d" % num)
test1(100)
# xx = set_func(test1)
***************执行结果***************
---这是权限验证1----
---这是权限验证2----
-----test1----100
同一个装饰器对多个函数进行装饰
def set_func(func):
print("---开始进行装饰")
def call_func(a):
print("---这是测试1----")
print("---这是测试2----")
func(a)
return call_func
@set_func # 相当于 test1 = set_func(test1)
def test1(num):
print("-----第%d条测试----" % num)
@set_func # 相当于 test2 = set_func(test2)
def test2(num):
print("-----第%d条测试----" % num)
test1(100)
test2(200)
***************执行结果***************
---开始进行装饰
---开始进行装饰
---这是测试1----
---这是测试2----
-----第100条测试----
---这是测试1----
---这是测试2----
-----第200条测试----
装饰器在没有调用函数之前已经装饰了
装饰器在调用函数之前,已经被python解释器执行了,所以要牢记 当调用函数之前 其实已经装饰好了,尽管调用就可以了
对不定长参数的函数进行装饰
def set_func(func):
print("---开始进行装饰")
def call_func(*args, **kwargs):
print("---这是另一组测试---")
# func(args, kwargs) # 不行,相当于传递了2个参数 :1个元组,1个字典
func(*args, **kwargs) # 拆包
return call_func
@set_func # 相当于 test1 = set_func(test1)
def test1(num, *args, **kwargs):
print("-----test1----%d" % num)
print("-----test1----" , args)
print("-----test1----" , kwargs)
test1(100)
test1(100, 200)
test1(100, 200, 300, mm=100)
***************执行结果***************
---开始进行装饰
---这是另一组测试---
-----test1----100
-----test1---- ()
-----test1---- {}
---这是另一组测试---
-----test1----100
-----test1---- (200,)
-----test1---- {}
---这是另一组测试---
-----test1----100
-----test1---- (200, 300)
-----test1---- {'mm': 100}
对带有返回值的函数进行装饰
def set_func(func):
def call_func(*args, **kwargs):
print("---这是另一组测试---")
# func(args, kwargs) # 不行,相当于传递了2个参数 :1个元组,1个字典
return func(*args, **kwargs) # 拆包
return call_func
@set_func # 相当于 test1 = set_func(test1)
def test1(num, *args, **kwargs):
print("-----test1----%d" % num)
print("-----test1----" , args)
print("-----test1----" , kwargs)
return "好的,我是返回值"
@set_func
def test2():
pass
ret = test1(100)
print(ret)
ret = test2()
print(ret)
***************执行结果***************
---这是另一组测试---
-----test1----100
-----test1---- ()
-----test1---- {}
好的,我是返回值
---这是另一组测试---
None
多个装饰器对同一函数进行装饰
def add_qx(func):
print("---开始进行第一111个装饰器---")
def call_func(*args, **kwargs):
print("---这是第一111个的功能----")
return func(*args, **kwargs)
return call_func
def add_xx(func):
print("---开始进行第二222个装饰器---")
def call_func(*args, **kwargs):
print("---这是第二222个的功能----")
return func(*args, **kwargs)
return call_func
@add_qx
@add_xx
def test1():
print("------test1------")
test1()
***************执行结果***************
---开始进行第二222个装饰器---
---开始进行第一111个装饰器---
---这是第一111个的功能----
---这是第二222个的功能----
------test1------
装饰时,@add_qx 的底下不是一个函数,然后就开始执行@add_xx ,等 @add_xx 装饰好时,相当于一个函数,这时@add_qx底下是一个函数,所以就可以进行装饰了。
- 多个装饰器装饰函数时 从最下面的函数开始往上调用
def set_func_1(func):
def call_func():
# "<h1>haha</h1>"
return "<h1>" + func() + "</h1>"
return call_func
def set_func_2(func):
def call_func():
return "<td>" + func() + "</td>"
return call_func
@set_func_1
@set_func_2
def get_str():
return "haha"
print(get_str())
***************执行结果***************
<h1><td>haha</td></h1>
使用类当做装饰器
# def set_func_1(func):
# def call_func():
# # "<h1>haha</h1>"
# return "<h1>" + func() + "</h1>"
# return call_func
class Test(object):
def __init__(self, func):
self.func = func
def __call__(self):
print("这里是装饰器添加的功能.....")
return self.func()
@Test # 相当于get_str = Test(get_str)
def get_str():
return "haha"
print(get_str())
***************执行结果***************
这里是装饰器添加的功能.....
haha