python函数修饰器(decorator)
python语言本身具有丰富的功能和表达语法,其中修饰器是一个非常有用的功能。在设计模式中,decorator能够在无需直接使用子类的方式来动态地修正一个函数,类或者类的方法的功能。当你希望在不修改函数本身的前提下扩展函数的功能时非常有用。
简单地说,decorator就像一个wrapper一样,在函数执行之前或者之后修改该函数的行为,而无需修改函数本身的代码,这也是修饰器名称的来由。
关于函数
在Python中,函数是first class citizen,函数本身也是对象,这意味着我们可以对函数本身做很多有意义的操作。
将函数赋值给变量:
def greet(name):
return "hello "+name greet_someone = greet
print greet_someone("John") # Outputs: hello John
函数内定义函数:
def greet(name):
def get_message():
return "Hello " result = get_message()+name
return result print greet("John") # Outputs: Hello John
函数可以作为参数传给其他函数:
def greet(name):
return "Hello " + name def call_func(func):
other_name = "John"
return func(other_name) print call_func(greet) # Outputs: Hello John
函数可以返回其他函数(函数产生函数):
def compose_greet_func():
def get_message():
return "Hello there!" return get_message greet = compose_greet_func()
print greet() # Outputs: Hello there!
内部函数可以访问外部包scope(enclosing scope)
def compose_greet_func(name):
def get_message():
return "Hello there "+name+"!" return get_message greet = compose_greet_func("John")
print greet() # Outputs: Hello there John!
需要注意的是:这种情况下python仅仅允许"只读"访问外部scope的变量
开始创作我们的decorator
函数的修饰器就是已知函数的wrapper.将上述函数的好功能运用起来就能制作我们的decorator.
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name) def p_decorate(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper my_get_text = p_decorate(get_text) print my_get_text("John") # <p>Outputs lorem ipsum, John dolor sit amet</p>
这就是我们的第一个修饰器。一个函数接收另一个函数作为参数,并且产生一个新的函数,修正参数函数的功能并添加新功能,并且返回一个"generated"新函数,这样我们后面就可以在任何地方使用这个新创建的函数了。我们也可以将修饰器函数直接赋值给参数函数名本身,这样就覆盖了原来的函数!
get_text = p_decorate(get_text)
print get_text("John")
# Outputs lorem ipsum, John dolor sit amet
另外一点需要注意的是:被修饰的函数get_text具有一个name参数,我们必须在wrapper函数中传入那个参数。
python的修饰符语法糖
在上面的例子中我们通过get_text=p_decorate(get_text)的方式覆盖了get_text从而形成了有新功能的同名函数,这个显得有点啰嗦,python提供了简洁清晰的对应语法。比如:
def p_decorate(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper @p_decorate
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name) print get_text("John") # Outputs <p>lorem ipsum, John dolor sit amet</p>
在上面的例子代码中,@符号后面的是修饰器本身,紧跟后面的则是将被修饰的函数(将隐含着赋值覆盖操作)。这种语法等价于使用@后面的修饰器先对get_text修饰,并且返回产生的新函数替代被修饰的函数名。后面直接用被修饰的函数名调用,但是却有了新的功能!
现在,我们希望再添加两个其他的函数来修饰get_text分别再增加一个div和strong tag
def p_decorate(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper def strong_decorate(func):
def func_wrapper(name):
return "<strong>{0}</strong>".format(func(name))
return func_wrapper def div_decorate(func):
def func_wrapper(name):
return "<div>{0}</div>".format(func(name))
return func_wrapper # 基础用法:
get_text = div_decorate(p_decorate(strong_decorate(get_text)))
#等价于:
@div_decorate
@p_decorate
@strong_decorate
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name) print get_text("John") # Outputs <div><p><strong>lorem ipsum, John dolor sit amet</strong></p></div>
需要注意的是修饰器的顺序是有关系的。如果顺序不同,则结果也不同。
method修饰
python中类的方法是一个首参数为self指针的函数。我们可以和普通函数一样去做修饰,但是需要注意的是必须在wrapper函数中考虑self指针参数。
def p_decorate(func):
def func_wrapper(self):
return "<p>{0}</p>".format(func(self))
return func_wrapper class Person(object):
def __init__(self):
self.name = "John"
self.family = "Doe" @p_decorate
def get_fullname(self):
return self.name+" "+self.family my_person = Person()
print my_person.get_fullname()
一个更好的方案是调整代码使得我们的修饰器对于函数或者method同样适用。这可以通过通过将args和*kwargs放到wrapper函数中作为参数来实现,这样可以接受任意个数的参数或者keyword型参数。
def p_decorate(func):
def func_wrapper(*args, **kwargs):
return "<p>{0}</p>".format(func(*args, **kwargs))
return func_wrapper class Person(object):
def __init__(self):
self.name = "John"
self.family = "Doe" @p_decorate
def get_fullname(self):
return self.name+" "+self.family my_person = Person() print my_person.get_fullname()
向decorator传入参数
def tags(tag_name):
def tags_decorator(func):
def func_wrapper(name):
return "<{0}>{1}</{0}>".format(tag_name, func(name))
return func_wrapper
return tags_decorator @tags("p")
def get_text(name):
return "Hello "+name print get_text("John") # Outputs <p>Hello John</p>
在这个例子中,貌似又更加复杂了一点,但是带来了更多的灵活性。decorator必须仅接受一个被修饰的函数为参数,这也是为什么我们必须再外包裹一层从而接受那些额外的参数并且产生我们的decorator的原因。这个例子中tags函数是我们的decorator generator
调试decorated function
从上面的描述可知,decorators负责包裹被修饰的函数,这带来一个问题就是如果要调试代码可能有问题,因为wrapper函数并不会携带原函数的函数名,模块名和docstring等信息,比如基于以上的例子,如果我们打印get_text.__name__则返回func_wrapper而不是get_text,原因就是__name__,__doc__,__module__这些属性都被wrapper函数所(func_wrapper)重载。虽然我们可以手工重置(在func_wrapper),但是python提供了更好的办法:
functools
functools模块包含了wraps函数。wraps也是一个decorator,但是仅仅用于更新wrapping function(func_wrapper)的属性为原始函数的属性(get_text),看下面的代码:
from functools import wraps def tags(tag_name):
def tags_decorator(func):
@wraps(func)
def func_wrapper(name):
return "<{0}>{1}</{0}>".format(tag_name, func(name))
return func_wrapper
return tags_decorator @tags("p")
def get_text(name):
"""returns some text"""
return "Hello "+name print get_text.__name__ # get_text
print get_text.__doc__ # returns some text
print get_text.__module__ # __main__
何时使用decorator?
在上面的例子中仅仅罗列了修饰器的基础用法,实际上这个机制是非常强大有用的,总的来说,decorator在你希望在不修改函数本身代码的前提下扩展函数的功能时非常有用。
一个经典的例子timeout修饰函数:
https://wiki.python.org/moin/PythonDecoratorLibrary#Function_Timeout
timeout修饰符产生函数的定义:
import signal
import functools class TimeoutError(Exception): pass def timeout(seconds, error_message = 'Function call timed out'):
def decorated(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message) def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result return functools.wraps(func)(wrapper) return decorated
使用:
import time @timeout(1, 'Function slow; aborted')
def slow_function():
time.sleep(5)
https://www.thecodeship.com/patterns/guide-to-python-function-decorators/
https://wiki.python.org/moin/PythonDecoratorLibrary
python函数修饰器(decorator)的更多相关文章
- Python 函数修饰器
# 一.用函数修饰函数 #!/usr/bin/python3 def decorate_func(func): def call(*args, **kwargs): print('you have c ...
- Python 函数修饰符(装饰器)的使用
Python 函数修饰符(装饰器)的使用 1. 修饰符的来源修饰符是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等. 修饰符是解决这类问题的绝佳设计, ...
- Python函数装饰器原理与用法详解《摘》
本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...
- python 类修饰器
1. 修改类函数. 场景: 如果要给一个类的所有方法加上计时,并打印出来.demo如下: # -*- coding:utf-8 -*- import time def time_it(fn): &qu ...
- ES2017中的修饰器Decorator
前面的话 修饰器(Decorator)是一个函数,用来修改类的行为.本文将详细介绍ES2017中的修饰器Decorator 概述 ES2017 引入了这项功能,目前 Babel 转码器已经支持Deco ...
- Python函数装饰器高级用法
在了解了Python函数装饰器基础知识和闭包之后,开始正式学习函数装饰器. 典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过 ...
- python函数修饰符@的使用
python函数修饰符@的作用是为现有函数增加额外的功能,常用于插入日志.性能测试.事务处理等等. 创建函数修饰符的规则:(1)修饰符是一个函数(2)修饰符取被修饰函数为参数(3)修饰符返回一个新函数 ...
- python函数-装饰器
python函数-装饰器 1.装饰器的原则--开放封闭原则 开放:对于添加新功能是开放的 封闭:对于修改原功能是封闭的 2.装饰器的作用 在不更改原函数调用方式的前提下对原函数添加新功能 3.装饰器的 ...
- 修饰器Decorator
类的修饰 许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为.目前,有一个提案将这项功能,引入了 ECMAScript. @testable class MyTestableCl ...
随机推荐
- margin折叠及hasLayout && Block Formatting Contexts
margin折叠的产生有几个条件: 这些margin都处于普通流中,并在同一个BFC中: 这些margin没有被非空内容.padding.border 或 clear 分隔开: 这些margin在垂直 ...
- 【数组】kSum问题
一.2Sum 思路1: 首先对数组排序.不过由于最后返回两个数字的索引,所以需要事先对数据进行备份.然后采用2个指针l和r,分别从左端和右端向中间运动:当l和r位置的两个数字之和小于目标数字targe ...
- Android 开发工具类 12_PullXmlTools
xml 格式数据 <?xml version="1.0" encoding="UTF-8"?> <user-list> <user ...
- Chapter 3 Phenomenon——10
It took six EMTs and two teachers — Mr. Varner and Coach Clapp — to shift the van far enough away fr ...
- Spring Boot 的彩色日志
springboot的彩色日志灰常漂亮, 看起来也很舒服. 但是自定义的日志就是一纯白色的, 丑到不行. 所以就copy他的彩色日志来养眼: <!-- 彩色日志 --> <!-- 彩 ...
- GBDT多分类示例
相当于每次都是用2分类,然后不停的训练,最后把所有的弱分类器来进行汇总 样本编号 花萼长度(cm) 花萼宽度(cm) 花瓣长度(cm) 花瓣宽度 花的种类 1 5.1 3.5 1.4 0.2 山鸢尾 ...
- elasticsearch的join查询
1.概述 官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/joining-queries.html 两种类型的 ...
- vue2.x中请求之前数据显示以及vuex缓存的问题
在项目中遇到两个问题,简单的做个笔记来记录自己解决的问题,可能不是很好的处理办法,欢迎提出,自己还在不断优化中... 第一个是vue在加载页面的时候,会先加载静态资源,这个时候数据还没有请求回来,用户 ...
- firefox native extension -- har export trigger
这两天想学习下如何在运行自动化脚本时去capture http traffic,google看到一篇博客介绍用browser mob proxy或者firefox+firebug+netexport, ...
- Sass进阶之路,之一(基础篇)
Sass 学习Sass之前,应该要知道css预处理器这个东西,css预处理器是什么呢? Css预处理器定义了一种新的语言将Css作为目标生成文件,然后开发者就只要使用这种语言进行编码工作了.预处理器通 ...