闭包和装饰器是Python中非常重要的一种语法格式,在日常工作中应用非常广泛。
首先,我先为大家简单的介绍一下闭包的概念。
闭包:闭包是在函数嵌套的基础上,内层函数使用到外层函数的变量,且外层函数返回内层函数的引用的一种语法格式。
闭包的基本格式,代码实现:
def outer():
num = 0
def inner():
# 使用外部函数的变量
print(num)
return inner # 返回inner函数在内存空间中的地址
# 将outer函数的返回值赋值给变量f,也就是说将f指向outer函数中的inner函数的内存地址
f = outer()
# 调用inner函数
f()
我们学习闭包的作用,其实就是为了学习装饰器。
那么,我们先来了解一下什么是装饰器。
装饰器就是通过闭包来对指定的函数进行功能的扩充,装饰器在函数定义时就会执行。
装饰器的特点:
1.不修改指定修饰函数的代码,
2.不修改指定修饰函数的调用方式,
3.给指定修饰函数增加相应的功能。
装饰器的种类:
1.普通装饰器
普通装饰器就是闭包语法格式的基础上,满足装饰器的特点的一种语法格式。
普通装饰器的代码实现:
def decorator(func):
# 此时func指向的是show函数
def inner(*args,**kwargs):
# 装饰的代码
ret = func()
return ret
return inner
@decorator # 原理为 show = decorator(show)
# 此时show指向的是decorator函数中的inner函数
def show():
print('show run....')
# 此时相当于调用decorator函数中的inner函数,并函数的返回值赋值给ret
ret = show()
图示如下:
1.1 多个装饰器装饰同一个函数:
多个装饰器装饰同一个函数的执行过程,先执行距离需要装饰函数近的装饰器,然后在执行距离需要装饰函数远的装饰器。
通过代码实现:
def make_div(func):
"""对被装饰的函数的返回值 div标签"""
# func指向是p中的inner函数的内存空间地址
def inner():
return "<div>" + func() + "</div>"
return inner
def make_p(func):
"""对被装饰的函数的返回值 p标签"""
# func 指向的是content函数的内存空间地址
def inner():
return "<p>" + func() + "</p>"
return inner
# 装饰过程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
# 当执行make_div的语法糖时,传递的是make_p中的inner函数的内存空间地址
@make_div
# make_div相当于装饰make_p修饰后的函数
@make_p # 按照顺序从下向上依次装饰,其根据是装饰器只能装饰已有函数# 当执行make_p的语法糖时,传递的是connet函数的内存空间地址
def content():
# content指向是p中的inner函数内存空间地址
return "人生苦短"
result = content()
print(result)
代码解读:
@make_div
@make_p
def show():
pass
执行流程:
1.@make_p装饰show函数
2.@make_div装饰@make_p修饰后的函数也就是make_p中的inner函数
2.类装饰器
类装饰器就是通过一个类来装饰函数,相较于普通装饰器,类装饰器可以使用父类中的方法。
类装饰器的代码实现:
class Decorator(object):
# 使用init方法来接收需要装饰的函数
def __init__(self, func):
# func指向show函数所在的内存空间地址
# 使用__func指向需要装饰的函数的内存空间地址
self.__func = func
# 可以让类的对象通过对象名()的方式被调用
def __call__(self, *args, **kwargs):
# 此时相当于调用了show函数
ret = self.__func(*args, **kwargs)
return ret
@Decorator # 原理是 show = Decorator(show)
# 此时show指向的是Decorator类的对象的内存地址
def show():
print('show run .....')
# 此时表示Decorator对象被调用,并接受show函数的返回值
ret = show()
在以上代码中,类中的init方法就相当于闭包中的外层函数用来接收被装饰的函数,call方法就相当于闭包中内层函数用来编写装饰代码以及调用被装饰的函数。
图示如下:
3.带有参数的装饰器
带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,...)
代码实现:
# 设置路由表,将字典当做路由表
router ={}
def set_args(url):
# url 传递的参数
def set_func(func):
# func --> show方法的内存空间地址
def inner():
func()
# 将inner函数的内存空间地址添加到路由表中
# key = url
# value = inner函数的内存空间地址
router[url] = inner
# 返回inner函数的内存空间地址
return inner
# 返回set_func函数的内存空间地址
return set_func
# 装饰器在函数定义时就会执行
# 1.先执行set_args('/show.html'),
# 相当于先调用set_args函数并进行参数的传递,
# 最后得到set_args的返回值也就是set_func
# 2.在执行@运算符,相当于执行装饰器@set_func
# @set_func --> show = set_func(show)
# 此时show ---> set_args.set_func.inner
@set_args('/show.html')
def show():
print('show run ....')
def error():
print('error run....')
def browser(url):
func = error
if url in router:
func = router[url]
# func()相当于调用了字典中符合条件的函数
func()
if __name__ == '__main__':
browser('/show.html')
代码解读:
@set_args('/show.html')
def show():
pass
其中@set_args('/show.html'),按照运算符优先级应该先执行()也就是先执行set_atgs('/show.html'),然后再执行@运算符。
执行流程是:
1.先执行set_args('/show.html'),相当于调用set_args()函数,并进行传参;
2.接受set_args()函数的返回值,并结合@运算数(@set_func),对show函数进行装饰。