python 装饰器

1 为什么要用装饰器

在介绍装饰器以前,我们先介绍一个写代码需要遵循的原则:开发封闭原则(OCP,Open Closed Principle)。一但产品上线,就尽量避免修改源代码。但在后期,也会添加各种需求。所以在设计之初,需要预留一些接口,用来作修改的手段。装饰器也应运而生。
开发封闭原则:已经实现的功能代码不允许被修改,但可以被扩展已经实现的功能代码不允许被修改,但可以被扩展。
封闭:已实现的功能代码块
开放:对扩展开发


# 2 装饰器的作用 装饰器(decorator)的作用,**在不修改源代码以及调用方式的情况下,给源码加入一些新功能。**

3 哪些能作装饰器

任何可调用的对象都可以做装饰器,如函数、类。
那被装饰的对象又有哪些要求?
和装饰器一样,只要是可调用的对象,也都可以做被装饰对象。

4 装饰器有哪几种

先从简单的开始讲,不适用装饰器是如何实现的:
我们都知道在python中函数是第一类对象,可以作为参数,也可以赋值给其他变量。

import time
def log(func):
    def swapper():
        start=time.clock()
        func()
        end=time.clock()
        print("run time is:",end-start)
    return swapper

def index():
    print("this is index")

index=log(index)
index()

result:
this is index
run time is: 2.285910487477409e-05

1 无参装饰器

上面的代码我们使用装饰器,就变成:

import time
def log(func):  #1 #3
    def swapper(): #4
        #print(func.__name__)    #index
        start=time.clock()   #8
        func()      #9  #11
        end=time.clock()   #12
        print("run time is:",end-start)  #13
    return swapper  #5

@log   #index=log(index)  #2  #6
def index():
    #print(index.__name__)    #swapper
    print("this is index")   #10

index()   #7  #14

    不难看出,@log等价于index=log(index)。其实@log的定义就是,将@正下方的函数名作为log()的参数,并作为接受返回值的对象。所以遇到@log,就把它理解为index=log(index),当执行到#5时,return swapper,也就是index=swapper,所以在第#9执行原index(),index.__name__打印的是swapper。而在第#4,形参func实际上接收的是index,所以func.__name__打印的是index。

我们再来看另一种情况,如果在index(),添加形参怎么做:

import time
def log(func):
    def swapper(arg):
        #print(func.__name__)
        start=time.clock()
        func(arg)
        end=time.clock()
        print("run time is:",end-start)
    return swapper

@log
def index(name):
    print("this is %s's index"%name)

index("abc")

result:
this is abc's index
run time is: 2.7057715974222393e-05

    如果要接收任意参数,把swapper(arg)改为swapper(*args,**args),func(arg)改为func(*args,**args)

2 有参装饰器

要给内部函数传递一个或多个变量,能想到的一个闭包,一个定义全局命名空间的变量。同时又希望函数能携带这一个或多个变量的信息,只能是闭包。所以我们只需要在已有的装饰器外面再包一层函数,并把这一个或多个变量作为形参。这样内部函数就能获取到了。

import time
def log2(type):
    def log(func):
        def swapper(*args,**kwargs):
            if type=="file":
                start=time.clock()
                res=func(*args,**kwargs)
                end=time.clock()
                print("run time is :",end-start)
                return res
            elif type=="sql":
                print("other")
        return swapper
    return log

@log2(type="file") #log #home=log(home)
def home(name):   #home=log(home)
    print("%s 's personal page!"%name)

home("aaa")

result:
aaa 's personal page!
run time is : 2.7057715974222393e-05

3 补充

我们从“1 无参装饰器”种不难看出func.__name__和index.__name__的值并不一样。

import time

def log(func):
    def swapper(*args,**kwargs):
        print("function swapper:",func.__name__)
        start=time.clock()
        res=func(*args,**kwargs)
        end=time.clock()
        print("run time is :",end-start)
        return res
    return swapper

@log
def now():
    print("function now:",now.__name__)
    print("2017-4-10")

now()

result:
function swapper: now
function now: swapper
2017-4-10
run time is : 1.679444439779321e-05

加入functools.warps(func)

import functools
import time

def log(func):
    @functools.wraps(func)
    def swapper(*args,**kwargs):
        print("function swapper:",func.__name__)
        start=time.clock()
        res=func(*args,**kwargs)
        end=time.clock()
        print("run time is :",end-start)
        return res
    return swapper

@log
def now():
    print("function now:",now.__name__)
    print("2017-4-10")

now()
result:
function swapper: now
function now: now
2017-4-10
run time is : 1.77274690865595e-05

@functools.wraps(func)也是一个装饰器,作用是,把原始函数的__name__等属性复制到 它所修饰的正下方的函数wrapper()中。除了函数名,还有__module__、__doc__等属性。

如何解除装饰器

from functools import wraps
import time

def log(func):
    @wraps(func)
    def swapper(*args,**kwargs):
        print("function swapper:",func.__name__)
        start=time.clock()
        res=func(*args,**kwargs)
        end=time.clock()
        print("run time is :",end-start)
        return res
    return swapper

@log
def now():
    print("function now:",now.__name__)
    print("2017-4-10")

now()
print("-----")
now.__wrapped__()

执行结果:

function swapper: now
function now: now
2017-4-10
run time is : 3.032331653110589e-05
-----
function now: now
2017-4-10

若装饰器是通过@wraps来实现的,就可以通过__wrapper__属性来访问原始函数。
最后要说的是,并不是所有的装饰器都使用了 @wraps ,因此这里的方案并不全部适用。特别的,内置的装饰器 @staticmethod 和 @classmethod 就没有遵循这个约定 (它们把原始函数存储在属性 func 中)。

原文地址:https://www.cnblogs.com/yangzhenwei123/p/6759205.html