修饰器

NOTE

1.函数对象有一个__name__属性,可以拿到函数的名字:

#!/usr/bin/env python3

def now():
print('2017/2/19') def main():
f = now
f()
print(now.__name__)
print(f.__name__) if __name__ == '__main__':
main()
sh-3.2# ./decorator1.py
2017/2/19
now
now

2.增强now函数的功能,又不想重新对now函数进行定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,装饰器就是一个返回函数的高阶函数:

#!/usr/bin/env python3

def log(func):
def wrapper():
print('call %s():' % func.__name__)
return func()
return wrapper @log
def now():
print('2017/2/19') def main():
f = now
f()
print(now.__name__)
print(f.__name__) if __name__ == '__main__':
main()
sh-3.2# ./decorator1.py
call now():
2017/2/19
wrapper
wrapper

把@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数

now -> now() 

=> 

now -> wrapper()

把函数名看成指向函数的指针变量就好了。

3.将wrapper()函数的参数定义修改为(*args, **kw),使wrapper()函数可以接受任意参数的调用。

4.如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。

eg.使用Flask web框架的时候,需要传入参数(路径)制定路由。

@app.route('/')
def index()
return '<h1>Hello</h1>'

这种传入参数的实现是三个函数的嵌套:

def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s()' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator @log('wasdns')
def now():
print('2017/2/19')
wasdns now()
2017/2/19
wrapper
wrapper

@log('wasdns')

now = log('wasdns')(now)

首先执行log('wasdns'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

一层一层解开。

5.函数也是Object,它有__name__等属性,但是在上面折腾之后,它的__name__属性变为:

wrapper

避免有些依赖函数签名的代码执行出错 => 需要把原始函数的__name__等属性复制到wrapper()函数中 => 调用Python内置的functools.wraps。

def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
sh-3.2# ./decorator1.py
call now():
2017/2/19
now
now
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s()' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
sh-3.2# ./decorator2.py
wasdns now()
2017/2/19
now
now

加在最里层的函数首部(最后返回的是最里层的函数),修改其属性就达到了我们的目的。

decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。

Practice

请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志。

#!/usr/bin/env python3

import functools

def log1(func):
print('begin call')
func()
def wrapper(*args, **kw):
print('end call')
return wrapper @log1
def func():
print('Hey Girl') def main():
func() if __name__ == '__main__':
main()
sh-3.2# ./decorator3.py
begin call
Hey Girl
end call

再思考一下能否写出一个@log的decorator,使它既支持:

@log
def f():
pass

又支持:

@log('execute')
def f():
pass

2017/2/19

最新文章

  1. 服务器部署多个tomcat经验
  2. Java数据结构之队列的实现以及队列的应用之----简单生产者消费者应用
  3. Leetcode 155 Min Stack
  4. POJ2533——Longest Ordered Subsequence(简单的DP)
  5. 【设计模式】学习笔记17:代理模式之保护代理与Java反射
  6. js 判断数组中是否存在
  7. Hadoop-2.2.0中文文档—— Common - CLI MiniCluster
  8. JavaScript 获取CSS媒体查询信息
  9. Nginx Configure时配置
  10. 使用JDBC获取能自动增加的主键
  11. C#基础、基础知识点(新人自我总结,开启java学习之路)
  12. 数据结构与算法(c++)——跳跃表(skip list)
  13. .net core 运行时事件(Runtime Events)
  14. Gson反序列化Map,整型自动转换为浮点型
  15. java实现将包含多个&lt;REC&gt;的文件拆成若干只包含一个&lt;REC&gt;的文件
  16. Edusoho之LAMP环境搭建
  17. Spark学习笔记——数据读取和保存
  18. sublime text syntaxdef
  19. ASP.NET Identity系列02,在ASP.NET MVC中增删改查用户
  20. 菜鸟学Java(九)——Servlet的基本配置

热门文章

  1. vux报错二
  2. SenchaTouch调用纯数字键盘
  3. BluePrint和ORM
  4. 什么是真正的APM?
  5. eclipse项目更换svn共享库
  6. ValueError: Only call `sparse_softmax_cross_entropy_with_logits` with named a
  7. Java Thread 如何处理未捕获的异常?
  8. 【开发者笔记】python
  9. 概率图模型PFM——无向图
  10. Django组件拾忆