目标
如果能准确的表述下侧代码的结果
就不用往下看啦,想必你已经掌握了 Python 装饰器
def outer (func) : def inner () : print("1" ) print("foo---%s" % func) return "2" return inner def foo () : print("3" ) return "4" print("===foo====" ) print(foo) print("===foo()====" ) print(foo()) print("===outer(foo)====" ) print(outer(foo)) print("===outer(foo())====" ) print(outer(foo())) print("===outer(foo())()====" ) print(outer(foo())())
先认真回忆下之前函数的相关章节内容不难但可能有点绕
===foo==== <function foo at 0x7fcae64dbea0 > ===foo ( )==== 3 4 ===outer (foo )==== <function outer .<locals >.inner at 0x7fcae64dbe18 > ===outer (foo( ))==== 3 <function outer .<locals >.inner at 0x7fcae645d598 > ===outer (foo( ))( )==== 3 1 foo ---42
没答出来,或对装饰器有疑惑,那就即可开始吧
第一部分 前奏 针对于开头的那块代码逐步分析下,究竟再内存中是如何运行的❓
执行顺序是什么❓
强烈推荐网址:代码可视化执行过程
这几行代码主要为了加深理解代码的执行过程
、函数名
、函数调用
等等
结合代码与下侧图示
,重温那些我们习以为常的东西。
def outer (func) : def inner () : print("1" ) print("foo---%s" % func) return "2" return inner def foo () : print("3" ) return "4" print("===foo====" ) print(foo) print("===foo()====" ) print(foo()) print("===outer(foo)====" ) print(outer(foo)) print("===outer(foo())====" ) print(outer(foo())) print("===outer(foo())()====" ) print(outer(foo())())
我觉得,先把这些看似简单的东西理清楚,对接下来,装饰器的理解更容易些
第二部分 装饰器【基础】 1 装饰器是什么 装饰器(Decorator):可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出。原则:开放封闭
分类
函数装饰器;
类的装饰器。
最近公司也在用装饰器,不过是再 Nodejs 中用的; 举个别的例子。因为某种原因,需要对每个接口进行认证 、记录日志 ,如何快速高效的实现这样的需求 ❓
可能有几种思路
在基类中写相应的方法,如果有的话;
写两个函数【认证的、日志的】,在每个接口中添加;
等等思路
2 简单的装饰器 但今天只能选装饰器 ,因为不能偏离主题啦,描述了需求就开始干吧!
def outer (func) : print("装饰器启动,自动执行" ) def inner () : print("统一认证" ) result = func() print("记录日志信息" ) return result return inner @outer def f1 () : print("接口1" ) f1()
返回值:
大致和第一部分的例子有点类似,不同的是
inner 函数返回了个函数
多个@outer
@outer 中的@
是注解语法糖;把 f1 作为参数传给 outer ,今儿满足我们的开放封闭的原则
;
其解释器会解释成下面这样的语句:*@outer <=> outer(f1)
翻译成函数调用, 我们用新的例子来对比一下,其实就是下侧这样的
def f2 () : print("接口2" ) outer(f2)() ''' 装饰器启动,自动执行 统一认证 接口1 记录日志信息 '''
了解了上述的调用含义, @outer <=> outer(f1)
注: 传的只是函数名
整体的流程
自上而下执行,将函数outer
在内存注册;
执行到@outer 时,将函数名f1
作为参数传给@outer
;
调用 outer 函数,执行打印【装饰器启动,自动执行】;
函数 inner 在内存注册,内存地址:[xxxx] ,返回【inner 函数名】,
将函数f1
在内存注册;其实是直接指向 [xxxx]
调用 f1(), 直接调用第五步的 [xxxx] , 也就是第四步的;
打印【统一认证】;
result = func()执行回调 【f1】函数, 打印:接口 1,没有返回值,result = None
回到inner()
,打印记录日志信息
;
返回 None,OVER!
注
:完整的有 17 步,动图还不会制作,可以前往–>代码可视化网址 **,查看完整的步骤。
实在是想不出来,如何用图来表示装饰器,有相应的图示,请介绍。
比较喜欢这段代码, 挺有意思的。O(∩_∩)O 哈哈~来源
会自动打印结果,因为采用了装饰器
def fuck (fn) : print "fuck %s!" % fn.__name__[::-1 ].upper() @fuck def wfg () : pass
第三部分 装饰器【进阶】 如果没有看明白第二部分,还是这回去,沉下心,专注的、认真的读下去,真的不难,好好分析下流程,一步一个脚印,踩实了!
只介绍最基础的代码,就是耍流氓
1 多个装饰器 下侧代码来源
def decorator_a (func) : print('Get in decorator_a' ) def inner_a (*args, **kwargs) : print('Get in inner_a' ) return func(*args, **kwargs) return inner_a def decorator_b (func) : print('Get in decorator_b' ) def inner_b (*args, **kwargs) : print('Get in inner_b' ) return func(*args, **kwargs) return inner_b @decorator_b @decorator_a def f (x) : print('Get in f' ) return x * 2 f(10 ) ''' Get in decorator_a Get in decorator_b Get in inner_b Get in inner_a Get in f '''
调用时,函数的顺序
顺序等同于==> decorator_b(decorator_a(f)) ,返回的是inner_b,所以执行的时候是先执行inner_b
然后在执行【decorator_a(f)】返回的inner_a .最终在调用f(1)的时候,函数inner_b输出’Get in inner_b’
执行inner_a输出Get in decorator_a,最后执行func(),即f
2 多参多装饰器 为了更加深入的了解,装饰器,摘录酷 壳 博客 上的一段代码,好像官网实例代码 也不少;
def makeHtmlTag (tag, *args, **kwds) : def real_decorator (fn) : css_class = " class='{0}'" .format(kwds["css_class" ]) \ if "css_class" in kwds else "" def wrapped (*args, **kwds) : return "<" +tag+css_class+">" + fn(*args, **kwds) + "</" +tag+">" return wrapped return real_decorator @makeHtmlTag(tag="b", css_class="bold_css") @makeHtmlTag(tag="i", css_class="italic_css") def hello () : return "hello world" print(hello()) ''' <b class='bold_css'><i class='italic_css'>hello world</i></b> '''
3 再深入一些 表示还没有研究这段代码,先分享出来 摘录酷 壳 博客 上的代码
from functools import wrapsdef memo (fn) : cache = {} miss = object() @wraps(fn) def wrapper (*args) : result = cache.get(args, miss) if result is miss: result = fn(*args) cache[args] = result return result return wrapper @memo def fib (n) : if n < 2 : return n return fib(n - 1 ) + fib(n - 2 )
Python函数告一段落了