闭包:https://zhuanlan.zhihu.com/p/93846887
装饰器:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
https://www.cnblogs.com/yaoqingzhuan/p/10628592.html
python参考手册:相关章节
一.预备知识
1.作用域、内嵌函数、生命周期
python中的每个函数都是一个新的作用域,或者理解为命名空间
一般而言,函数中定义的变量、内嵌函数等,其生命周期即所在函数的作用域,当函数执行完毕后,函数内所定义变量、内嵌函数等都应该会消失。而在下一次调用时,又会被重新创建。
2.变量搜索
当在函数中访问一个新的变量时,python会在当前命名空间中寻找该变量是否存在。如果不存在则会从上一级命名空间中搜寻,直到顶层命名空间。比如:
image.png
但是在函数中对一个变量进行定义或者赋值时,python只会在当前命名空间中搜寻该变量,如果不存在,则会创建一个新的变量。如果上一级命名空间中存在同名的变量,那么上一级同名变量会在当前作用于中被覆盖。
image.png
二.闭包
1.定义
将组成函数的语句和这些语句执行环境打包在一起时,得到的对象称为闭包。
2.使用场景
函数在python中是第一类对象。也就是说可以把他们当作参数传递给其他函数、放在数据结构中、以及作为函数的返回结果等。(这些场景都会触发闭包)
把函数当作数据处理时,它将隐式地携带与定义该函数的周围环境相关的信息(该函数所需的所有信息)。
3.举例说明
- 示例1:闭包影响自由变量的绑定方式
# foo.py
x = 42
def callf(func):
return func()
# 调用代码
import foo
x = 37
def helloworld():
return "Hello world, x is %d" %x
fool.callf(helloworld)
输出结果:
Hello world, x is 37
-
示例2:闭包捕捉函数执行所需的整个环境
image.png
从作用域的角度:foo实际上是调用了内嵌函数inner(), 当执行到inner中的print(x)语句时,在inner()中没有搜寻到x,然后会在outer()命名空间中搜寻,找到x后进行打印。
从生命周期的角度,foo的值为outer函数的返回值,当执行foo()时,outer函数已经执行完毕了,此时其作用于内定义的变量x也应该已经销毁,因此执行foo()时,当执行到print(x)时,应该会出错。但实际上并没有。
这其实是python支持函数闭包的特性。
三.装饰器
闭包会捕捉内部的环境信息,因此还可以用于包装现有函数。
1.定义
装饰器是一个函数(也可以是类或者对象),其主要用途是包装另一个函数或者类。这种包装的首要目的是光明睁大的修改或者增强被包装对象的行为。
2.分类
1.函数装饰器
根据装饰器本身是否接受参数,可以分为:带参数的装饰器和不带参数的装饰器
A.不带参数的装饰器:包装的函数func本身,作为装饰器的入参。
@trace
def square(x):
return x*x
#其等价于:
def square(x):
return x*x
square = trace(square)
# trace代码的实现如下
# 备注:内部函数callf作为返回值,是一个闭包
def trace(func):
def callf(*args, **kwargs):
debug_log.write('calling args:%s' % args)
r = func(*args, **kwargs)
debug_log.write('%s returned' % r)
return r
return callf
B.带参数的装饰器(多一层对装饰器参数的处理)
首先:参数param作为装饰器的入参,返回第一层函数:W。(第一层:对装饰器本身参数的处理)
然后:将包装的函数func本身,作为W的入参,传递执行。(第二层:对包装函数的处理,不带参数的装饰器,只有该层)
# 装饰器带参数的版本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
# 其含义是:
now = log('execute')(now)
# 装饰器本身不带参数的版本:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 其含义是:
now = log(now)
2.使用类作为装饰器
类也可以作为装饰器。
image.png
3.使用对象作为装饰器
根据装饰器的语法,对象也可以作为装饰器。使用对象装饰器有时候会更加灵活,例如能够方便的定制和添加参数。
image.png
4.装饰器装饰类
接受类作为输入,并返回类作为输出
image.png
备注:
由此可见,@本身是一个python的语法糖。它只是按照固定格式进行展开,展开后只要符合python语法,不论是:类、对象、函数等,都允许灵活的搭配使用。
网友评论