修饰器

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

Python学习札记(二十六) 函数式编程7 修饰器的更多相关文章

  1. Python学习札记(二十五) 函数式编程6 匿名函数

    参考:匿名函数 NOTE 1.Python对匿名函数提供了有限的支持. eg. #!/usr/bin/env python3 def main(): lis = list(map(lambda x: ...

  2. Python学习札记(二十四) 函数式编程5 返回函数

    参考:返回函数 NOTE 1.高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. eg.求和函数 #!/usr/bin/env python3 def calsums(*args): a ...

  3. Python学习札记(三十六) 面向对象编程 Object Oriented Program 7 __slots__

    参考:slots NOTE 1.动态语言灵活绑定属性及方法. #!/usr/bin/env python3 class MyClass(object): def __init__(self): pas ...

  4. Python学习札记(二十) 函数式编程1 介绍 高阶函数介绍

    参考: 函数式编程 高阶函数 Note A.函数式编程(Functional Programming)介绍 1.函数是Python内建支持的一种封装,我们通过一层一层的函数调用把复杂任务分解成简单的任 ...

  5. Python学习札记(二十二) 函数式编程3 filter & SyntaxError: unexpected EOF while parsing

    参考: filter Problem SyntaxError: unexpected EOF while parsing 遇到该语法错误,一般是由于 括号不匹配 问题. Note 1.filter 用 ...

  6. Python学习(二十六)—— Django基础一

    转载自:http://www.cnblogs.com/liwenzhou/p/8258992.html 一.Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的 ...

  7. Python学习札记(三十九) 面向对象编程 Object Oriented Program 10

    参考:使用枚举类 NOTE #!/usr/bin/env python3 from enum import Enum def main(): Mouth = Enum('Mouth', ('Jan', ...

  8. Python学习札记(三十八) 面向对象编程 Object Oriented Program 9

    参考:多重继承 NOTE #!/usr/bin/env python3 class Animal(object): def __init__(self, name): self.name = name ...

  9. Python学习札记(三十五) 面向对象编程 Object Oriented Program 6

    参考:实例属性和类属性 NOTE Python是动态语言,根据类创建的实例可以任意绑定属性. class Student(object): def __init__(self, name): self ...

随机推荐

  1. Map集合中value()方法与keySet()、entrySet()区别 《转》

    在Map集合中 values():方法是获取集合中的所有的值----没有键,没有对应关系, KeySet(): 将Map中所有的键存入到set集合中.因为set具备迭代器.所有可以迭代方式取出所有的键 ...

  2. mysql查杀会话

    root登陆mysql,查看会话(show processlist\G;): mysql> kill

  3. onethink文章详情如何做上一篇和下一篇!

    其实很简单,如果要做上一篇和下一篇,只要知道当前文章ID的前一个ID和后一个ID即可: //上一篇文章 $prewhere = array(); $prewhere['id'] = array('LT ...

  4. 使用NUget发布自己的dll

    一:Nuget控制台有几个常用命令 Get-Package 获取当前项目已经安装的类库 Install-Package 安装指定类库,命令格式如下:Install-Package 类库ID,示例:PM ...

  5. 160227、javascript特效

    1.给网页设定快捷键 js: function getkey(){     event = event || window.event;     url = "www.baidu.com&q ...

  6. 如何在不改SQL的情况下优化数据库

    主题简介 在数据库运维中我们会遇到各种各样的问题,这些问题的根源可能很明显,也可能被某种表象掩盖而使我们认不清.所以运维面临的两大问题就是,第一我们没有看清本质,第二应用不允许修改.那么我们如何解决这 ...

  7. windows 文件查找 大小:>250M

    win7怎么快速查找大文件_百度经验 https://jingyan.baidu.com/article/acf728fd299ffff8e510a333.html

  8. Python多进程编程(转)

    原文:http://www.cnblogs.com/kaituorensheng/p/4445418.html 阅读目录 1. Process 2. Lock 3. Semaphore 4. Even ...

  9. C++ new 长度为0的数组

    在C++中可以new一个长度为0的数组,通过下面的语句: char* p = new char[0]; 指针p中保存一个非NULL的地址,但是你不能够对p指向的内存进行写入,因为p的内存长度为0, 该 ...

  10. 【PHP】善用php-fpm的慢执行日志slow log,分析php性能问题

    (转)善用php-fpm的慢执行日志slow log,分析php性能问题  众所周知,mysql有slow query log,根据慢查询日志,我们可以知道那些sql语句有性能问题.作为mysql的好 ...