Python第二十六天 python装饰器html
装饰器
Python 2.4 开始提供了装饰器( decorator ),装饰器做为修改函数的一种便捷方式,为工程师编写程序提供了便利性和灵活性
装饰器本质上就是一个函数,这个函数接受其余函数做为参数,并将其以一个新的修改后的函数进行替换。python
装饰器的做用
一、注入参数。为函数提供默认参数,生成新的参数等
二、记录函数的行为。能够统计函数的调用次数,缓存函数的结果,计算函数调用耗费的时间
三、预处理与后处理
四、修改调用时的上下文编程
函数能够赋值给另一个变量名
函数能够嵌套
函数对象能够做为另一个函数的参数
装饰器仅仅是利用前面的这些Python 知识加上Python 语法实现的一种高级语法缓存
函数对象
在Python 语言中, def 语句定义了一个函数对象,并将其赋值给函数名。
也就是说,函数名其实只是一个变量,这个变量引用了这个函数对象。
所以,咱们能够将函数赋值给另一个变量名,经过这个新的变量名调用函数。app
def say_hi(): print ("HI") hello = say_hi hello()
嵌套函数
在Python 语言中, def 语句是一个实时执行的语句,当它运行的时候会建立一个新的函数对象,并将其赋值给一个变量名
这里所说的变量名就是函数的名称。由于def 是一个语句,所以,函数定义能够出如今其余语句之中。编程语言
def outer(x , y) : def inner () : return x + y return inner f = outer(l , 2) print(f())
在这个例子中,咱们定义了一个名为outer 的函数, 并在outer 函数内部定义了inner 函数
outer 函数以返回值的形式返回inner 函数,咱们将返回值保存在变量f 中,f 引用的是outer 函数内部的inner 函数
因此,当咱们调用函数f 时,实际是调用的inner 函数函数
回调函数
回调函数是指将函数做为参数传递给另一个函数,并在另一个函数中进行调用。
回调函数并非Python 语言特有的,在各个编程语言中都存在。ui
def greeting(f): f() def say_hi(): print ("HI") def say_hello(): print("HELLO") greeting(say_hi) greeting(say_hello)
装饰器
在Python 的装饰器语法中,内层函数的参数是被装饰的函数参数,外层函数的参数是被装饰的函数。spa
def say_hi(): print("hi") def bread(f): def wrapper(*args, **kwargs ): print("begin call {0}".format(f.__name__)) f() print("finish call {0}".format(f.__name__)) return wrapper say_hi_copy = bread(say_hi) say_hi_copy()
上面这段代码的执行结果以下:
begin call say_hi
hi
finish call say_hipwa
改造为装饰器
@bread def say_hi(username='someone'): #被装饰函数,被装饰函数的参数必定要用关键字参数,不能用位置参数,不然不能装入字典!!!!!!! print("hi") print(username) def bread(f): #装饰器函数 def wrapper(*args, **kwargs ): print("begin") f() print("finish") print(kwargs.get("username")) #读取被装饰函数的参数,被装饰函数的参数必定要用关键字参数,不能用位置参数,不然不能装入字典 return wrapper say_hi() 这段程序和前面的程序做用如出一辙,产生的结果也相同。区别在于,前面的程序显示地调用了bread 函数来封装say_hi 函数 这段程序经过Python 的语法糖来封装say_hi函数。在Python 中, say_hi 函数定义语句前一行的"@bread"语句表示对该函数应用bread装饰器 其中,"@"是装饰器的语法,"bread"是装饰器的名称
获取正确函数属性
对于一个函数,咱们能够经过name 属性获得函数的名字,经过doc 属性获得函数的帮助信息。
可是, 一个被装饰器装饰过的函数,默认状况下,咱们经过doc 和name 获取属性时,获得的倒是装饰器中嵌套函数的信息
标准库的functools 模块中的wraps 装饰器。wraps 装饰器的做用是,复制函数属性至被装饰的函数
使用functools.wraps 装饰器装饰wrapper 函数。经过这样简单的修改就能够获取到add 函数的正确属性
from __future__ import print_function import time import functools def benchmark(func): @functools.wraps(func) def wrapper(*args , **kwargs): t = time.time() res = func(*args , ** kwargs) print(func.__name__, time.time() - t) return res return wrapper @benchmark def add (a, b): '''Calculate the sum of two numbers''' return a + b print(add.__name__) print(add.__doc__)
inspect 模块
inspect 模块提供了许多有用的函数来获取活跃对象的信息。其中, getcallargs 函数用来获取函数的参数信息
getcallargs 会返回一个字典,该字典保存了函数的全部参数,包括关键字参数和位置参数。
也就是说, getcallargs 可以根据函数的定义和传递给函数的参数,推测出哪个值传递给函数的哪个参数。
getcallargs 推测出这些信息之后,以一个字典的形式返回给咱们全部的参数和取值。
import functools import inspect def check_is_admin(func): @functools.wraps(func) def wrapper(* args, * *kwargs): func_args = inspect.getcallargs(func , *args , ** kwargs) if func_args.get('username')! = 'admin' : raise Exception("This user is not allowed to put/get elem") return f( * args, * *kwargs ) return wrapper
给装饰器传递参数
在Python 的装饰器语法中,内层函数的参数是被装饰的函数参数,外层函数的参数是被装饰的函数。
那么,若是装饰器自己也有参数应该怎么办呢?在Python 的装饰器语法中, 若是装饰器自己也有参数,则须要再嵌套一层函数。
这也是为何读者看到的Python装饰器有时候是两层嵌套, 有时候是三层嵌套的缘由
下面是一个带参数的装饰器。在这个装饰器的实现中,最外层的函数是装饰器的名称。
这个装饰器的做用是将被装饰的函数执行屡次。具体执行的次数由装饰器的参数指定
from __future__ import print_function def times(length=1): def bread(func): def wrapper(* args ,* *kwargs): for i in range(length): func(*args, **kwargs) # func表明被装饰函数sandwich(),因此被装饰函数有什么参数这里就要写什么,例如Django里面每一个view函数都有request参数,装饰view函数就要写成func(request, *args, **kwargs) return wrapper return bread @times(5) #5传入到length,执行5次sandwich def sandwich(name): print (name) sandwich('hello')
装饰器的前后顺序
例子中,never_cache就要先于login_required被调用。
decorators = [never_cache, login_required] @method_decorator(decorators, name='dispatch') class ProtectedView(TemplateView): template_name = 'secret.html'
或
@method_decorator(never_cache, name='dispatch') @method_decorator(login_required, name='dispatch') class ProtectedView(TemplateView): template_name = 'secret.html'
使用method_decorator有时会致使TypeError异常,由于参数传递的缘由。固然,通常人碰不到的,碰获得的人都不通常,都能本身查Django官方文档掌握问题缘由。
参考
http://www.liujiangblog.com/blog/37/
def record_functime(): ''' 函数执行时间统计装饰器 :return: ''' def stopwatch(callback): @functools.wraps(callback) def wrapper(*args, **kwargs): start = time.time() body = callback(*args, **kwargs) end = time.time() timelog = 'function name:'+callback.__name__ +'; execute time:' +str(start) +'; elapsed time:'+str(end - start) log_helper.info(timelog) return body return wrapper return stopwatch