#建模大赛预备#认真学习装饰器

illustration

#1.什么是闭包 装饰器的一个基本原理就是闭包,函数嵌套:

1def func1(x): 2 return x 3def func2(y): 4 print y 5a = func1(func2) 6a('hello')

输出为:

hello

上面的代码中,func1直接返回x,那么a=func2(),然后执行a('hello')等于直接执行func2("hello")

#2.基本函数装饰器入门 而装饰器就是在上面的闭包上实现的一个为函数添加功能的快捷方式: 先看代码:

1def printmessage(dosth): 2 def wrapper(number): 3 print 'we print pow(number,2)' 4 return dosth(number) 5 return wrapper 6 7@printmessage 8def dosomething(number): 9 return pow(number, 2) 10 11print dosomething(2)

第一,printmessage函数作为一个dosomething额外的功能用于打印消息,它的形式参数是目标函数
第二,wrapper的形参为printmessage提供参数,同时额外的功能在wrapper里实现,最后要返回wrapper(注意不是wrapper())
然后定义dosomething函数,并通过@使用printmessage这个装饰器
输出的结果为:

1we print pow(number,2) 24

实际上,装饰器的作用只是为了解决这一步:

1dosomething = printmessage(dosomething) 2print dosomething(2)

等号左边的dosomething是添加了输出功能的版本,而括号里的dosomething是最初定义的函数 之所以用装饰器,主要是为了减少代码重构时的修改,不用每一处都像上面一样用赋值写,只需要 在原来的函数上添加个@符号和对应的函数作为装饰器即可

#3.带参数的装饰器与不确定的参数 在上面的例子里,我没法选择输入值作为打印内容,如果我想打印的是其他内容而不是'we print pow(number,2)'
同时,上面的装饰器函数的形参只有一个,如果dosomething是多个形参,就无法使用了,那么解决方法是:

1def printmessage(message): #提供外部配置参数 2 def getmessage(func): #提供具体的函数功能 3 def wrapper(*args, **kwargs): #为具体的函数功能提供参数 4 print message 5 return func(*args, **kwargs) 6 return wrapper 7 return getmessage 8 9#@printmessage('fuck') 10def dosth(number, number1): 11 return pow(number, number1) 12 13a=printmessage('fuck') 14 15print a 16dosth = a(dosth) 17 18print dosth(2, 4)

问题1解决:通过多一层的嵌套,printmessage的形参是一个message,作为输出内容,同时,原来的使用函数作为形参的部分则交给内部的getmessage,事实上,如果只用闭包,也是外层提供不同的参数,里层提供函数的核心。因此getmessage的形参是func

问题2解决:wrapper函数的形参写成了*args和**kwargs,这样就可以满足不同形参数的函数的装饰需求,同时wrapper的形参负责为形参函数提供具体的参数,最后别忘了嵌套返回

参考使用不好的写法,可以看到闭包是怎么做的:

1a=printmessage('fuck') 2dosth = a(dosth)

首先是用printmessage输入参数,然后a=getmessage(),dosth=wrapper()

不过个人认为,添加的额外功能应该放在getmessage里,而wrapper只负责func的return,这样代码会更规范易懂

#4.docstring遗失? 另外,参考上面的代码,如果我们在dosth中加入docstring的话会怎样呢:

1def printmessage(dosth): 2 def wrapper(number): 3 print 'we print pow(number,2)' 4 return dosth(number) 5 return wrapper 6 7@printmessage 8def dosomething(number): 9 """ 10 Fuck the numbers 11 :param number: 12 :return: 13 """ 14 return pow(number, 2) 15 16print dosomething(2) 17print dosomething.__doc__

计算结果其实是:

1we print pow(number,2) 24 3None

意味着这种情况下,原函数的docstring还有__name__都被遗失了

这种情况下需要from functools import wraps,然后再wrapper前调用@wraps(dosth),复制dosomething的docstring过来

1from functools import wraps 2 3def printmessage(dosth): 4 @wraps(dosth) 5 def wrapper(number): 6 print 'we print pow(number,2)' 7 return dosth(number) 8 return wrapper

再次执行:

1we print pow(number,2) 24 3 Fuck the numbers 4 :param number: 5 :return:

这样就能看到原函数的docstring了

-------------------------------------------------------分割线------------------------------------------------------------------------------------------------------------------------------------------

#5.类装饰器 上面的是函数装饰器,那么还有类的装饰器:

1class Foo(object): 2 def __init__(self, func): 3 self._func = func 4 5 def __call__(self): 6 print ('class decorator runing') 7 self._func() 8 print ('class decorator ending') 9 10 11@Foo 12def bar(): 13 print ('bar') 14 15bar()

输出结果为:

1class decorator runing 2bar 3class decorator ending

只要实现了__call__,就可以变成一个可以调用的对象:

1class Edu(object): 2 def __call__(self, *args, **kwargs): 3 return "yeshi1" 4 5p = Edu() 6print p() 7#显示“yeshi1”

上面的代码是实例化了Edu类,然后调用p()时就会自动执行我们的代码,return一个值

同样,在上面的Foo类中,定义了__call__函数,于是Foo的实例就是可以调用的,调用时会自动执行func() 实际上,去掉@Foo,代码应该是这样的:

1class Foo(object): 2 def __init__(self, func): 3 self._func = func 4 def __call__(self): 5 print ('class decorator runing') 6 self._func() 7 print ('class decorator ending') 8 9def bar(): 10 print ('bar') 11 12bar = Foo(bar) 13bar()

这个似乎跟函数式的装饰器差不了多少,至少在调用闭包时没什么区别,用了bar函数来实例化Foo类并赋值给bar,bar的执行时就会打印那些信息。看来重点是实现__call__,而其他功能通过其他成员函数实现。

#6.函数装饰器+类参数 而我们还可以搭配函数装饰器和类使用,请参考此例子:

1class locker: 2 def __init__(self): 3 print("locker.__init__() should be not called.") 4 5 @staticmethod 6 def acquire(): 7 print("locker.acquire() called.(这是静态方法)") 8 9 @staticmethod 10 def release(): 11 print(" locker.release() called.(不需要对象实例)") 12 13def deco(cls): 14 '''cls 必须实现acquire和release静态方法''' 15 def _deco(func): 16 def __deco(): 17 print("before %s called [%s]." % (func.__name__, cls)) 18 cls.acquire() 19 try: 20 return func() 21 finally: 22 cls.release() 23 return __deco 24 return _deco 25 26@deco(locker) 27def myfunc(): 28 print(" myfunc() called.") 29 30myfunc()

上面的代码通过实现类的静态方法,在不需要实例化的情况下调用上锁和释放的功能,并通过嵌套函数实现带参装饰器,而装饰器形参为包含静态方法的类

#7.其他东西 python还有些内置的装饰器:@staticmathod、@classmethod、@property
分别是静态方法、类方法和属性装饰器,平常静态方法用得更多,但是在设计模式上回用到classmethod等,详细的介绍可以参考:
classmethod:classmethod
property:property

而关于多重装饰器的使用:

1@a 2@b 3@c 4def f ():

这个代码等效于:

f = a(b(c(f)))

reference:
装饰器

装饰器

装饰器

装饰器

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java获得今日零时零分零秒的时间(Date型)

publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS

Python之time模块的时间戳、时间字符串格式化与转换

Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y

#建模大赛预备#认真学习装饰器 - HelloWorld