“Python有什么好学的”这句话可不是反问句,而是问句哦。shell
主要是煎鱼以为太多的人以为Python的语法较为简单,写出来的代码只要符合逻辑,不须要太多的学习便可,便可从一门其余语言跳来用Python写(固然这样是好事,谁都但愿入门简单)。闭包
因而我便记录一下,若是要学Python的话,到底有什么好学的。记录一下Python有什么值得学的,对比其余语言有什么特别的地方,有什么样的代码写出来更Pythonic。一路回味,一路学习。app
修饰器英文是Decorator,函数
咱们假设这样一种场景:古老的代码中有几个非常复杂的函数F一、F二、F3...,复杂到看都不想看,反正咱们就是不想改这些函数,可是咱们须要改造加功能,在这个函数的先后加功能,这个时候咱们很容易就实现这个需求:学习
def hi(): """hi func,伪装是很复杂的函数""" return 'hi' def aop(func): """aop func""" print('before func') print(func()) print('after func') if __name__ == '__main__': aop(hi)
以上是非常简单的实现,利用Python参数能够传函数引用的特性,就能够实现了这种相似AOP的效果。spa
这段代码目前没有什么问题,接下来煎鱼加需求:需求为几十个函数都加上这样的先后的功能,而全部调用这些函数地方也要相应地升级。日志
看起来这个需求比较扯,恰恰这个需求倒是较为普遍:在调用函数的先后加上log输出、在调用函数的先后计算调用时间、在调用函数的先后占用和释放资源等等。code
一种比较笨的方法就是,为这几十个函数逐一添加一个入口函数,针对a函数添加一个a_aop函数,针对b函数添加一个b_aop函数...如此这样。问题也很明显:对象
因而接下来有请修饰器出场,修饰器能够统一地给这些函数加这样的功能:继承
def aop(func): """aop func""" def wrapper(): """wrapper func""" print('before func') func() print('after func') return wrapper @aop def hi(): """hi func""" print('hi') @aop def hello(): """hello func""" print('hello') if __name__ == '__main__': hi() hello()
以上aop函数就是修饰器的函数,使用该修饰器时只要在待加函数上一行加@修饰器函数名
便可,如实例代码中就是@aop
。
加上了@aop
后,调用新功能的hi函数就喝原来的调用同样:就是hi()
而不是aop(hi)
,也意味着全部调用这些函数的地方不须要修改就能够升级。
简单地来讲,大概修饰器就是以上的这样子。
对于新手来讲,上面例子中,@
就是同样奇怪的东西:为何这样子用就能够实现煎鱼需求的功能了。
其实咱们还能够不用@
,煎鱼换一种写法:
def hi(): """hi func""" print('hi') def aop(func): """aop func""" def wrapper(): """wrapper func""" print('before func') func() print('after func') return wrapper if __name__ == '__main__': hi() print('') hi = aop(hi) hi()
上面的例子中的aop函数就是以前说过的修饰器函数。
如例子main函数中第一次调用hi函数时,因为hi函数没叫修饰器,所以咱们能够从输出结果中看到程序只输出了一个hi而没有先后功能。
而后煎鱼加了一个hi = aop(hi)
后再调用hi函数,获得的输出结果和加修饰器的同样,换言之:
@aop
等效于hi = aop(hi)
所以,咱们对于@
,能够理解是,它经过闭包的方式把新函数的引用赋值给了原来函数的引用。
有点拗口。aop(hi)
是新函数的引用,至于返回了引用的缘由是aop函数中运用闭包返回了函数引用。而hi
这个函数的引用,原本是指向旧函数的,经过hi = aop(hi)
赋值后,就指向新函数了。
以上的例子中,咱们都假设被调函数是无参的,如hi、hello函数都是无参的,咱们再看一眼煎鱼刚才的写的修饰器函数:
def aop(func): """aop func""" def wrapper(): """wrapper func""" print('before func') func() print('after func') return wrapper
很明显,闭包函数wrapper中,调用被调函数用的是func()
,是无参的。同时就意味着,若是func是一个带参数的函数,再用这个修饰器就会报错。
@aop def hi_with_deco(a): """hi func""" print('hi' + str(a)) if __name__ == '__main__': # hi() hi_with_deco(1)
就是参数的问题。这个时候,咱们把修饰器函数改得通用一点便可,其中import了一个函数(也是修饰器函数):
from functools import wraps def aop(func): """aop func""" @wraps(func) def wrap(*args, **kwargs): print('before') func(*args, **kwargs) print('after') return wrap @aop def hi(a, b, c): """hi func""" print('test hi: %s, %s, %s' % (a, b, c)) @aop def hello(a, b): """hello func""" print('test hello: %s, %s' % (a, b)) if __name__ == '__main__': hi(1, 2, 3) hello('a', 'b')
这是一种很奇妙的东西,就是在写修饰器函数的时候,还用了别的修饰器函数。那也没什么,毕竟修饰器函数也是函数啊,有什么所谓。
思路到了这里,煎鱼不由思考一个问题:修饰器函数也是函数,那函数也是应该能传参的。函数传参的话,不一样的参数能够输出不一样的结果,那么,修饰器函数传参的话,不一样的参数会怎么样呢?
其实很简单,修饰器函数不一样的参数,能生成不一样的修饰器啊。
如,我此次用这个修饰器是把时间日志打到test.log
,而下次用修饰器的时候煎鱼但愿是能打到test2.log
。这样的需求,除了写两个修饰器函数外,还能够给修饰器加参数选项:
from functools import wraps def aop_with_param(aop_test_str): def aop(func): """aop func""" @wraps(func) def wrap(*args, **kwargs): print('before ' + str(aop_test_str)) func(*args, **kwargs) print('after ' + str(aop_test_str)) return wrap return aop @aop_with_param('abc') def hi(a, b, c): """hi func""" print('test hi: %s, %s, %s' % (a, b, c)) @aop_with_param('pppppp') def hi2(a, b, c): """hi func""" print('test hi: %s, %s, %s' % (a, b, c)) if __name__ == '__main__': hi(1, 2, 3) print('') hi2(2, 3, 4)
一样的,能够加一个参数,也能够加多个参数,这里就不说了。
大道同归,逻辑复杂了以后,人们都喜欢将函数的思惟层面抽象上升到对象的层面。缘由每每是对象能拥有多个函数,对象每每能管理更复杂的业务逻辑。
显然,修饰器函数也有对应的修饰器类。写起来也没什么难度,和以前的生成器同样简单:
from functools import wraps class aop(object): def __init__(self, aop_test_str): self.aop_test_str = aop_test_str def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): print('before ' + self.aop_test_str) func() print('after ' + self.aop_test_str) return wrapper @aop('pppppp') def hi(): print('hi')
看得出来,这个修饰器类也不过是多了个__call__
函数,而这个__call__
函数的内容和以前写的修饰器函数一个样!而使用这个修饰器的方法,和以前也同样,同样的如例子中的@aop('pppppp')
。
甚至,煎鱼过于无聊,还试了一下继承的修饰器类:
class sub_aop(aop): def __init__(self, sub_aop_str, *args, **kwargs): self.sub_aop_str = sub_aop_str super(sub_aop, self).__init__(*args, **kwargs) def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): print('before ' + self.sub_aop_str) super(sub_aop, self).__call__(func)() print('after ' + self.sub_aop_str) return wrapper @sub_aop('ssssss', 'pppppp') def hello(): print('hello') if __name__ == '__main__': hello()
大家猜猜结果怎么样?
先这样吧
如有错误之处请指出,更多地请关注造壳。