闭包和装饰器
一、闭包
1.闭包的定义
闭包是一种高阶函数, 函数名代表函数的引用, 函数名可以作为另一个函数的参数和返回值作用
-
- 一个外部函数内定一个内函数
- 内函数使用外函数的临时变量
- 外函数返回内函数的引用
2.闭包的作用
隐藏功能的实现细节
闭包比类更加节省资源
但是闭包不能完全代替类
3.注意点
当在内函数中使用外函数的局部变量时,可直接使用
当在内函数中修改外函数的局部变量时,需要使用nonlocal声明
二、装饰器
1.装饰器概述
功能:在已有函数基础上,在不改变函数代码及调用方式的基础前提下,为函数添加额外的功能
语法:@xxxx
装饰器原理:被装饰函数名指向了闭包中的内函数 # func = wrapper(func)
2.装饰器的几种例子
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang """ 根据被装饰函数的定义形式不同(参数返回值) 可以将装饰器定义成四种 # 1. 无参 无返回值 # 2. 有参 无返回值 # 3. 无参 有返回值 # 4. 有参 有返回值 """ # 1. 无参 无返回值 def set_func1(func): def inner(): print("调用前的装饰语句...") func() print("调用后的装饰语句...") return inner @set_func1 def show1(): print("show1...") # 2. 有参 无返回值 def set_func2(func): def inner(string): print("调用前的装饰语句...") func(string) print("调用后的装饰语句...") return inner @set_func2 def show2(string): print("show2...", string) # 3. 无参 有返回值 def set_func3(func): def inner(): print("调用前的装饰语句...") res = func() print("调用后的装饰语句...") return res return inner @set_func3 def show3(): return "show3..." # 4. 有参 有返回值 def set_func4(func): def inner(string, n): print("调用前的装饰语句...") res = func(string, n) print("调用后的装饰语句...") return res return inner @set_func4 def show4(string, n): return "show4..." + string + str(n) if __name__ == '__main__': # show1() # show2('hello world') # print(show3()) print(show4('哈哈', 666))
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang """ 根据被装饰函数的定义形式不同(参数返回值) 可以将装饰器定义成四种 """ # def set_func(func): # def inner(*args, **kwargs): # print("装饰语句1...") # res = func(*args, **kwargs) # print("装饰语句2...") # return res # return inner # 使用通用装饰器实现计算时间的功能 import time # 定义一个外函数,实现用作装饰器的闭包 def set_func(func): def inner(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print("公用 %.2f 秒" % (end - start)) return res return inner @set_func def show1(): print("show1...") @set_func def show2(string): print("show2...", string) @set_func def show3(): return "show3..." @set_func def show4(string, n): return "show4..." + string + str(n) @set_func def show5(n1, n2, n3): return n1 + n2 + n3 if __name__ == '__main__': show1() show2('hello world') print(show3()) print(show4('哈哈', 666)) print(show5(1,2,3))
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang """ 使用类来实现来实现装饰器 """ # 实现装饰器类 # 实现两个方法 # init方法,用来接收被装饰函数的引用 # call方法, 用让被装饰函数名调用时, 可以执行, 因为使用类装饰器后 # 被装饰函数就不在指向原函数对象, 而是指向类的实例对象 import time class CountTime(object): def __init__(self, func): self.__func = func # 这个方法才可以被装饰函数执行时, 真正执行的方法, 也就是闭包的内函数 def __call__(self, *args, **kwargs): start = time.time() res = self.__func(*args, **kwargs) end = time.time() print("公用了 %s 秒" % ( end - start)) return res @CountTime # show1 = CountTime(show1) def show1(): print("show1...") @CountTime def show2(string): print("show2...", string) @CountTime def show3(): return "show3..." @CountTime def show4(string, n): return "show4..." + string + str(n) @CountTime def show5(n1, n2, n3): return n1 + n2 + n3 if __name__ == '__main__': show1() show2('hello world') print(show3()) print(show4('哈哈', 666)) print(show5(1,2,3))
3.装饰器传参
概念:在使用过程中,除被装饰器函数外,还需要额外传入数据时,需要进行装饰器传参
过程步骤:
1. 先执行 set_args('参数') 得到 set_args 函数中的返回值,也就是 set_fun 函数的引用
2. 然后返回的引用和 @ 进行组合,变成装饰器形式 @set_fun , 但是这时 set_fun 函数因为是返回的闭包引用,所以保留了args的参数值
3. 再调用 show 的时候, show 还是指向的 wrapper 函数,但是在这个函数中,可以使用外面两层函数的变量或参数
4. 无论在何时,闭包有几层,最终被装饰的函数永远指向 wrapper 函数
代码示例:
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Mr.yang """ """ # 路由表 router_table = {} # 装饰器 def router(url): def set_func(func): def inner(*args, **kwargs): return func(*args, **kwargs) # 实现将 url 作为key, 然后将 被装饰的函数作为值, 保存在路由表 router_table[url] = inner return inner return set_func # 定义页面功能函数 @router('index.html') def index(): print('Index...') @router('center.html') def center(): print('Center...') @router('new.html') def news(): print('New...') def other(): print("404...") # 模拟请求的方法 def request_url(url): # 根据传入的请求地址, 来找到对应的页面 print("请求的地址是 %s" % url) print("页面的内容是:") # 判断地址是否是某一个 # if url == 'index.html': # index() # elif url == 'center.html': # center() # elif url == 'news': # news() # else: # other() if url in router_table: func = router_table[url] else: func = other func() if __name__ == '__main__': request_url('index.html') request_url('center.html') request_url('new.html') request_url('aaa.html') print(router_table)