当前位置: 首页 > 后端技术 > Python

懂女朋友的心,不如懂Python的Python装饰器

时间:2023-03-26 02:01:54 Python

在Python中,函数可以作为参数传递给函数,也可以将函数复制到变量中,函数通过多变的。装饰器可以扩展一个函数的功能,为该函数做一个装饰器注解,可以为所有的函数提前执行装饰器中定义的函数,提高代码的复用度。所以现在才有了这样的一幕。签到互联网公司有各种岗位,比如程序员,前台...,程序员开电脑前需要签到,前台要早点开门(其实我也不知道是谁平时开门,这里假设为前台开门),也需要在前台开门前签到。换言之,签到是所有员工的第一个公共行为,因此签到功能可以提取为公共逻辑。很自然的会想到普通的函数调用方式,可以这样实现。defdi(f):print('%ssignin,drop...'%f.__name__)returnf()defboot():print('boot')defopen():print('打开door')if__name__=='__main__':"""程序员开机前,前台开门前,需要在门外用指纹机签到。"""di(boot)di(open)定义了一个函数di(f),它可以打印f.__name__,即f的函数名信息,同时返回f()的执行结果。注意:如果__name__作为模块导入,则module.__name__是模块本身的名称。如果模块本身作为脚本执行,则返回__main__。执行结果:开机签到,滴...开机签到,滴...开门。如果要调用的函数很多,会很麻烦,装饰器就派上用场了。简单装饰器和@grammatical糖装饰器:一种在代码执行期间动态添加功能的方法称为“装饰器”。简单的装饰器定义了一个di(f)方法,仍然把要执行的逻辑的函数作为参数传入,并在里面定义了一个包装函数,返回值就是f的执行结果。在if__name__=='__main__':中,调用了这个装饰器,不修改定义的函数,在运行时动态添加“登录”函数。importfunctools#Simpledecoratordefdi(f):"""程序员开机,前台开门前,需要用门外的指纹机签到。:paramf:传入一个函数:return:"""#将原函数的__name__等属性复制到wrapper()@functools.wraps(f)defwrapper():print('%ssignin,drop...'%f.__name__)returnf()returnwrapperdefboot():print('boot')defopen():print('openthedoor')if__name__=='__main__':#第一个,简单装饰器a=di(boot)a1=di(open)print(a.__name__)#wrapper的结果是加上@functools.wraps(f)a()a1()ofdi(boot)返回值a是wrapper函数,通过a()调用wrapper函数得到boot的返回值。同理di(open)也是如此。结果bootboot登录,di...bootopensignin,di...openthedoor因为di(boot)的返回值a是wrapper函数,所以print(a.__name__)的结果当然是wrapper,我们希望是boot。怎么办呢,functools.wraps(f)注解可以将原函数boot_name__的_包裹起来,其他属性复制到wrapper()中,并且这行代码注释也可以运行,那么print(a.__name__)的结果就是wrapper。第二,@语法糖也可以在函数上面应用装饰器,推荐。importfunctoolsdefdi(f):"""程序员开机前,前台开门前,需要在门外用指纹机签到。:paramf:传入一个函数:return:"""#将原函数的__name__等属性复制到wrapper()@functools.wraps(f)defwrapper():print('%ssignin,drop...'%f.__name__)returnf()returnwrapper#@语法糖@didefboot2():print('boot')@didefopen2():print('openthedoor')if__name__=='__main__':#第二种,@语法糖boot2()open2()@di标记相当于,a2=di(boot2)a2()。不用那么麻烦,因为@符号标记添加后,直接使用boot2()调用装饰器即可。结果boot2sign-in,beep...bootopen2sign-in,beep...开门业务逻辑函数需要参数业务逻辑函数可能需要参数,例如:defboot(name):print('%sboot'%name)那么,只需要修改之前的装饰器为:importfunctools#业务逻辑函数需要参数defdi(f):"》在程序员启动机器和前台开门前,fin入住需要在门外安装指纹机。:paramf:传入一个函数:return:"""#将原函数的__name__等属性复制到wrapper()@functools.wraps(f)defwrapper(*args,**kwargs):print('%s登录,删除...'%f.__name__)returnf(*args,**kwargs)returnwrapper@didefboot(name):print('%sboot'%name)if__name__=='__main__':boot('keguang')result:bootsignin,drop...keguang启动并在wrapper中加入*args,**kwargs参数,直接在wrapper中调用f(*args,**kwargs)boot.顺便说一下:*args:可以传入一个数组参数**kwargs:可以传入一个对应参数顺序的k-v对,数组参数在前。例如:deff(*args,**kwargs):print('args=',args)print('kwargs=',kwargs)print(f(1,2,3,a='a',b='b'))#result#args=(1,2,3)#kwargs={'a':'a','b':'b'}带参数的装饰器如果装饰器也带参数,比如员工上班早早上<9:00,我们可以称赞,so相当于只需要在wrapper中在前面的di(),di_args之外再放一层函数。使用这个参数importfunctools#decoratorwithparametersdefdi_args(time):defdi(f):"""程序员开机,前台开门前,需要用门外的指纹机签到门。:paramf:传入一个函数:return:"""#将原函数的__name__等属性复制到wrapper()@functools.wraps(f)defwrapper(*args,**kwargs):iftime<'9:00':print('真早,真好...')print('%scheckin,drop...'%f.__name__)returnf(*args,**kwargs)returnwrapperreturndi@di_args('8:00')defboot(name):print('%sboot'%name)if__name__=='__main__':boot('keguang')parameterin@di_args('8:00')可以传入,有点像java中的注解。最后可以通过boot('keguang')调用,结果:来的真早,太棒了。。boot登录,drop。。.keguangBoot类装饰器类装饰器主要依赖于类的__call__方法,当使用@形式将装饰器附加到函数时会调用该方法#Classdecoratorclassdi(object):def__init__(self,f):self._f=fdef__call__(self,*args,**kwargs):print('装饰器开始...')self._f()print('装饰器结束...')@didefboot():原理t('boot')if__name__=='__main__':boot()加上@di装饰器标志,会使用boot实例化di类,然后执行__call__函数,object代表这个类任何类型的参数都可以通过了。运行结果:decoratorstart...startupdecoratorend...一个典型的装饰器应用场景就是打日志。如果所有的逻辑都需要记录程序的运行状态,可以给这些逻辑(函数)设备加上日志模块装饰,就可以达到相应的目的。最后,如果文章对你有用,给作者点个赞吧。拒绝白嫖,从我做起