一、闭包函数
装饰器的本质是闭包函数,下面我就先来回顾一下闭包函数。
闭包函数:内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数,也就是说函数内部定义的函数就是闭包函数。
那么我们直接来看下闭包函数的代码:
def func_fuck():
name = "bug"
def inner():
print(name)
print(inner) # .inner at 0x02B8BCD8>
# 判断一个函数是否是闭包函数的做法是在闭包函数的上一级打印这个函数名+__closure__
# 如果打印的结果内部有cell at 这个元素,那么这个函数就是闭包函数
# inner.__closure__返回的是一个元组
print(inner.__closure__) # (,)
return inner
f = func_fuck()
# 执行了函数func,返回了inner,实际上inner是一个指向了inner()函数地址的变量
# 那么就相当于执行 f = inner,将f的指向了inner,具备了inner的地址
f()
# 那么实际上再执行f(),实际上是执行的是func函数内部的inner函数
print(f) # .inner at 0x02B8BCD8> |
上面是这个闭包函数的代码,下面这个是这个代码的执行顺序,可以清晰的看到执行的过程了。
闭包函数也可以嵌套来使用,只不过是中间多了一层推导过程罢了。
# 闭包函数的嵌套使用
def wrapper():
hobby = "Nothing"
def func():
name = "fuck"
def inner():
print(name, hobby)
return inner()
return func
f = wrapper()
i = f()
i()
闭包函数的应用,可以应用在爬虫领域,引用他人博客上的一个例子,让我们看看校花这个网站。
# 闭包函数的应用
from urllib.request import urlopen
def index():
url = "http://www.xiaohua100.cn/index.html"
def get():
return urlopen(url).read()
return get
xiaohua = index()
content = xiaohua()
print(content)
二、装饰器
什么是装饰器呢,装饰器就是一个函数,是闭包函数的一个延伸,其本质与闭包函数没有什么区别,只不过增加了语法糖@,让代码看起来更流畅。我们使用装饰器就是想让程序增加一些扩展的功能,但是不能改变原程序代码及函数调用方式,只能增加,我们先看一个简单的装饰器,不可避免的还是用测试函数时间来举例吧。
# 装饰器,检测函数执行时间
import time
def timer(func):
def inner():
start = time.time()
func() # 引用了外层函数的形参变量
print("the time time_check cost:", time.time() - start)
return inner
@timer # ===>>> time_check = timer(time_check)
def time_check():
print("this is time check function")
time.sleep(10)
time_check()
# 我们从@time这一行可以看到,这跟闭包并没有什么区别。
我们从上段代码可以知道,只有一个装饰器是很简单的,很容易就看出了多层的执行顺序,那么多个装饰器装饰一个函数呢,让我们来看看两个装饰器装饰一个函数吧。
# 多个装饰器装饰一个函数,探究一下这样的执行顺序的问题
def wrapper1(func): # wrapper1:
# func:
print("1:in wrapper1")
def inner1(): # inner1:.inner1 at 0x00D71030>
print("2:in inner1_before")
func() # func:
print("3:in inner1_after")
return inner1
def wrapper2(func): # wrapper2:
# func:.inner1 at 0x00D71030>
print("4:in wrapper2")
def inner2(): # inner2:.inner2 at 0x00D71078>
# func:.inner1 at 0x00D71030>
print("5:in inner2_before")
func() # 执行这个函数:.inner1 at 0x00D71030>
print("6:in inner2_after")
return inner2
@wrapper2
@wrapper1
def test(): # .inner2 at 0x00D71078>
# 装饰器装饰test后将test这个函数名指向了哪里?
# test = wrapper1(test) = inner1
# inner1 = wrapper2(inner1) = inner2
# test = inner2
print("7:this is the function test we use")
test()
上段代码是不是看起来很清晰,但是他内部执行时还是有点复杂的,没关系,来个图片看一看他的执行顺序。
其实我们一般来说用不到这么复杂的,像我例子中的一般inner上面的打印那段代码是不会写的,我们用装饰器的目的只是想给函数增加功能,只要在inner内部实现功能就好,如果只是在inner内部定制功能,那么其执行过程我们可以简要的概述一下:
真正执行的时候是test()函数执行,那么我们可以这样认为 { [ test ] } ,用括号来代替代码,左边最外层 { 是装饰器wrapper2的func()上面的代码,右边最外层是装饰器wrapper2的func()下面的代码,同理 [ ] 两个中括号分别代表装饰器wrapper1的func()的上面和下面的代码,然后我们就把执行顺序理解为 { [ test ] } ,意思是从左至右依次执行,多层的也是一样的,只不过又加了多层的括号来表示代码,如果看不懂上图的顺序,那么就按照这个括号的做法来记忆吧,一般情况下还是有用的。
到这里装饰器基本上就没什么问题了,再来看一段有关修改名字的代码。
# 带参数的装饰器及完善
from functools import wraps
def wrapper_para(func):
@wraps(func)
def inner(*args, **kwargs):
print("do something before")
ret = func(*args, **kwargs)
print("do something after")
return ret
return inner
@wrapper_para
def func_para(aim):
print(aim)
return "success"
t = func_para("this is test")
print(t)
# 打印结果
# do something before
# this is test
# do something after
# success
那么给装饰器加上装饰器能够实现么,可能实现么,告诉你,没得问题,还是看代码吧。
# 测试装饰器的使用记录
from functools import wraps
def wrapper_wrapper(func):
def inner(*args, **kwargs):
print("这是最外层装饰器,测试另一个装饰器,之前的动作")
ret = func(*args, **kwargs)
print("这是最外层装饰器,测试另一个装饰器,之后的动作")
return ret
return inner
def wrapper_para(func):
@wraps(func)
@wrapper_wrapper
def inner(*args, **kwargs):
print("do something before")
ret = func(*args, **kwargs)
print("do something after")
return ret
return inner
@wrapper_para
def func_para(aim):
print(aim)
return "success"
t = func_para("this is test")
print(t)
# 这是最外层装饰器,测试另一个装饰器,之前的动作
# do something before
# this is test
# do something after
# 这是最外层装饰器,测试另一个装饰器,之后的动作
# success
好了,今天就这样吧,自己和自己聊天也挺好玩的,如果你看到了这个文章,那么就是和你聊天了,哈哈,未谋面的朋友,祝你天天开心。