第四篇:装饰器
一、闭包函数
什么是闭包
内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包
def counter(): n=0 def incr(): nonlocal n x=n n+=1 return x return incr c=counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) #查看闭包的元素
闭包的意义与作用
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 #应用领域:延迟计算(原来我们是传参,现在我们是包起来) from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu=index('http://www.baidu.com') print(baidu().decode('utf-8'))
闭包优缺点:
1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
二、装饰器
本质是函数 为其他函数附加其它功能
原则:
1、不修改被修饰函数的源代码
2、不修改被修饰函数的调用方式
装饰器=高阶函数+函数嵌套+闭包
1、多个装饰器装饰同一个函数 def outer_0(func): print('正在装饰1') def inner(): print('aaa') return inner def outer2(func): print('正在装饰2') def inner(): print('bbb') return inner @outer_0 @outer2 def index(): pass #先执行最里面的装饰器,装的时候倒着装,执行的时候还是按照从上往下执行 正在装饰2 正在装饰1 aaa bbb def makeBold(fn): print('正在进行装饰') def wrapped(): print('1') return "<b>" + fn() + "</b>" return wrapped #定义函数:完成包裹数据 def makeItalic(fn): def wrapped(): print('2') return "<i>" + fn() + "</i>" return wrapped @makeBold #只要python解释器执行到了这个代码,那么就会自动的进行装饰,而不是等到调用的时候才装饰的 def test1(): return "hello world-1" @makeItalic def test2(): return "hello world-2" @makeBold <===>test3=makeBold(makeItalic(test3)) @makeItalic <===>test3=makeItalic(test3) def test3(): print('3') return "hello world-3" #<b><i>hello world-3</i></b> 在调用test3之前,已经完成了装饰 test3()====>1 2 3 2、对无参数的函数进行装饰 from time import ctime, sleep def timefun(func): def wrappedfunc(): print("%s called at %s"%(func.__name__, ctime())) func() return wrappedfunc @timefun <====>foo=timefun(foo) foo() def foo(): print("I am foo") 上面代码理解装饰器执行行为可理解成 foo = timefun(foo) 如果函数上面有@xxx 此行就可以省略 语法糖 #foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfunc foo() #调用foo(),即等价调用wrappedfunc() #内部函数wrappedfunc被引用,所以外部函数的func变量(自由变量)并没有释放 #func里保存的是原foo函数对象 3、装饰器对有参数的函数进行装饰 def func(functionName): print("---func---1---") def func_in(a, b):#如果a,b 没有定义,那么会导致test(11,22)的调用失败 print("---func_in---1---") functionName(a, b)#如果没有把a,b当做实参进行传递,那么会导致调用def test(a,b)函数失败 print("---func_in---2---") print("---func---2---") return func_in @func def test(a, b): print("----test-a=%d,b=%d---"%(a,b)) test(11,22) 4、装饰器对有返回值的函数进行装饰 def func(functionName): print("---func---1---") def func_in(): print("---func_in---1---") ret = functionName() #保存 返回来的haha print("---func_in---2---") return ret #把haha返回岛ret=test()处的调用 print("---func---2---") return func_in @func def test(): print("----test----") return "haha" ret = test() print("test return value is %s"%ret) 5、装饰器带有参数,在原有装饰器的基础上,设置外部变量 #带有参数的装饰器,能够起到在运行时,有不同的功能(当传入haha时执行这个方法 当传入heihei时执行另外一个方法) def func_arg(arg): def func(functionName): def func_in(): print("---记录日志-arg=%s--"%arg) if arg=="heihei": functionName() functionName() else: functionName() return func_in return func #1. 先执行func_arg("heihei")函数,这个函数return 的结果是func这个函数的引用 #2. @func #3. 使用@func对test进行装饰 @func_arg("heihei") def test(): print("--test--") 6、通用装饰器 def func(functionName): def func_in(*args, **kwargs): print("-----记录日志-----") ret = functionName(*args, **kwargs) return ret return func_in @func def test(): print("----test----") return "haha" @func def test2(): print("----test2---") @func def test3(a): print("-----test3--a=%d--"%a) ret = test() print("test return value is %s"%ret) a = test2() print("test2 return value is %s"%a) 装饰器什么时候进行装饰 只要python解释器执行到了@xxx这个代码,就会自动的进行装饰,而不是等到调用的时候才装饰的 当有多个装饰器包裹时,会先执行最里面的装饰器 装饰的时候倒着装
类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 __call__() 方法,那么这个对象就是callable的 class Test(): def __call__(self): print('call me!') t = Test() t() # call me __str__是print(t)时执行 类装饰器demo class Test(object): def __init__(self, func): print("---初始化---") print("func name is %s"%func.__name__) self.__func = func ===>指向test()函数 def __call__(self): print("---装饰器中的功能---") self.__func() ===>保存的test()函数 @Test <=======>Test(test) #执行到这时自动调用__init__方法 def test(): print("----test---") test() <=======>t=Test(test) t() 1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象,并且会把test这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量指向了test函数体 2. test函数相当于指向了用Test创建出来的实例对象 3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法 4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用,所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体 user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic={'username':None,'login':False} def auth(auth_type='filedb'): def auth_func(func): def wrapper(*args,**kwargs): print('认证类型是',auth_type) if auth_type == 'filedb': if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('用户名:').strip() passwd=input('密码:').strip() for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: current_dic['username']=username current_dic['login']=True res = func(*args, **kwargs) return res else: print('用户名或者密码错误') elif auth_type == 'ldap': print('鬼才特么会玩') res = func(*args, **kwargs) return res else: print('鬼才知道你用的什么认证方式') res = func(*args, **kwargs) return res return wrapper return auth_func @auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type --->index=auth_func(index) def index(): print('欢迎来到京东主页') @auth(auth_type='ldap') def home(name): print('欢迎回家%s' %name) # @auth(auth_type='sssssss') def shopping_car(name): print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) # print('before-->',current_dic) # index() # print('after--->',current_dic) # home('产品经理') shopping_car('产品经理')
wraps函数
使用装饰器时,有一些细节需要被注意。例如,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)。 添加后由于函数名和函数的doc发生了改变,对测试结果有一些影响 import functools def note(func): "note function" @functools.wraps(func) 当未写这段代码时 test.__doc__打印的是装饰器中的文档说明 def wrapper(): "wrapper function" print('note something') return func() return wrapper @note def test(): "test function" print('I am test') test() print(test.__doc__)
posted on 2018-05-23 11:59 liyancheng 阅读(97) 评论(0) 收藏 举报
浙公网安备 33010602011771号