day 11 闭包 + 闭包嵌套 + 装饰器
闭包:
闭包的作用:内部函数包括对外部作用于而非全局作用域的引用。该内部函数被称为闭包函数。
函数内部定义的函数被称为内部函数,
由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了。如果我们就是想拿怎么办呢?返回呀!
我们都知道函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?
是不是直接就把这个函数的名字返回就好了?
这才是闭包函数最常用的用法
def func(): name = 'eva' def inner(): print(name) return inner f = func() f() 判断闭包函数的方法__closure__ #输出的__closure__有cell元素 :是闭包函数 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() #输出的__closure__为None :不是闭包函数 name = 'egon' def func2(): def inner(): print(name) print(inner.__closure__) return inner f2 = func2() f2()
多个装饰器装饰一个函数(重点)
六,多个装饰器装饰一个函数。 def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函数之前要做的''') re = func(*args,**kwargs) if flag: print('''执行函数之后要做的''') return re return inner return timer @outer(False) def func(): print(111) func() 复制代码 返回顶部 多个装饰器装饰同一个函数 有些时候,我们也会用到多个装饰器装饰同一个函数的情况。 复制代码 def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()
闭包的嵌套:
def func(): name = '老男孩' def guess(): play = 'ball' def inner(): return name + play return inner return guess ret = func() x = ret() print(x())
闭包函数获取网络应用:
from urllib.request import urlopen def func(): url = 'https://www.taobao.com/' def inner(): return urlopen(url).read() return inner ret = func() print(ret())
装饰器:
什么是装饰器?
——装饰器本质上就是一个python函数,它可以让其他函数再不做任何代码变动的情况下,增加额外的功能
装饰器的返回值也是一个函数
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景
二,装饰器的形成过程。
现在我有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:
import time
def func1():
print('in func1')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1)
func1()
但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。

import time
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@timer #==> func1 = timer(func1)
def func1():
print('in func1')
func1()
刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?
def timer(func):
def inner(a):
start = time.time()
func(a)
print(time.time() - start)
return inner
@timer
def func1(a):
print(a)
func1(1)
装饰器——带参数的装饰器
import time
def timer(func):
def inner(*args,**kwargs):
start = time.time()
re = func(*args,**kwargs)
print(time.time() - start)
return re
return inner
@timer #==> func1 = timer(func1)
def func1(a,b):
print('in func1')
@timer #==> func2 = timer(func2)
def func2(a):
print('in func2 and get a:%s'%(a))
return 'fun2 over'
func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))
上面的装饰器已经非常完美了,但是有我们正常情况下查看函数信息的方法在此处都会失效:
def index():
'''这是一个主页信息'''
print('from index')
print(index.__doc__) #查看函数注释的方法
print(index.__name__) #查看函数名的方法
如何解决呢?
from functools import wraps
def deco(func):
@wraps(func) #加在最内层函数正上方
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
@deco
def index():
'''哈哈哈哈'''
print('from index')
print(index.__doc__)
print(index.__name__)
装饰器的叠加:
叠放装饰器 执行顺序是什么 如果一个函数被多个装饰器修饰,其实应该是该函数先被最里面的装饰器修饰后(下面例子中函数main()先被inner装饰,变成新的函数),变成另一个函数后,再次被装饰器修饰 def outer(func): print('enter outer', func) def wrapper(): print('running outer') func() return wrapper def inner(func): print('enter inner', func) def wrapper(): print('running inner') func() return wrapper @outer @inner def main(): print('running main') if __name__ == '__main__': main() #返回结果 # enter inner <function main at 0x000001A9F2BCDF28> # enter outer <function inner.<locals>.wrapper at 0x000001A9F2BD5048> # running outer # running inner # running main 类装饰器 相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。 import time class Foo(object): def __init__(self, func): self._func = func def __call__(self): start_time=time.time() self._func() end_time=time.time() print('spend %s'%(end_time-start_time)) @Foo #bar=Foo(bar) def bar(): print ('bar') time.sleep(2) bar() #bar=Foo(bar)()>>>>>>>没有嵌套关系了,直接active Foo的 __call__方法 标准库中有多种装饰器 例如:装饰方法的函数有property, classmethod, staticmethod; functools模块中的lru_cache, singledispatch, wraps 等等 from functools import lru_cache from functools import singledispatch from functools import wraps functools.wraps使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子: def foo(): print("hello foo") print(foo.__name__)# foo def logged(func): def wrapper(*args, **kwargs): print (func.__name__ + " was called") return func(*args, **kwargs) return wrapper @logged def cal(x): resul=x + x * x print(resul) cal(2) #6 #cal was called print(cal.__name__)# wrapper print(cal.__doc__)#None #函数f被wrapper取代了,当然它的docstring,__name__就是变成了wrapper函数的信息了。 好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。 from functools import wraps def logged(func): @wraps(func) def wrapper(*args, **kwargs): print(func.__name__ + " was called") return func(*args, **kwargs) return wrapper @logged def cal(x): return x + x * x print(cal.__name__) # cal 使用装饰器会产生我们可能不希望出现的副作用, 例如:改变被修饰函数名称,对于调试器或者对象序列化器等需要使用内省机制的那些工具,可能会无法正常运行; 其实调用装饰器后,会将同一个作用域中原来函数同名的那个变量(例如下面的func_1),重新赋值为装饰器返回的对象;使用@wraps后,会把与内部函数(被修饰函数,例如下面的func_1)相关的重要元数据全部复制到外围函数(例如下面的decorate_inner) from functools import wraps def decorate(func): print('running decorate', func) @wraps(func) def decorate_inner(): print('running decorate_inner function', decorate_inner) return func() return decorate_inner @decorate def func_1(): print('running func_1', func_1) if __name__ == '__main__': func_1() #输出结果 #running decorate <function func_1 at 0x0000023E8DBD78C8> # running decorate_inner function <function func_1 at 0x0000023E8DBD7950> # running func_1 <function func_1 at 0x0000023E8DBD7950>


浙公网安备 33010602011771号