Python第二十六天 python装饰器

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
相关文章
相关标签/搜索