Python-装饰器

目录:https://www.jianshu.com/p/863c446364a8

装饰器

我们谈装饰器之前先来说说开放封闭原则。

开放封闭原则:

1.开放---对代码的拓展开放          2.封闭---对源码的修改封闭

接着我们再来谈装饰器,什么是装饰器?

装饰器是在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。

装饰器完全遵守开放封闭原则。

装饰器同样拥有三前提:作用域、高阶函数、闭包。学会这三个东西再来学装饰器将是水到渠成。

让我们来做一个银行存款的例子。

def a():

    print("-----存款中-----")

a()

我们现在有一个需求,给他增加一个密码验证的过程。

def a():

    print("-----存款中-----")

def b():

    print("-----密码验证中-----")

b()

a()

或者:

def a():

    b()

    print("-----存款中-----")

def b():

    print("-----密码验证中-----") 

a()

这显然违背了开放封闭原则,而且造成了代码冗余。

现在我们对它进行第一次优化:

这一次优化是将存款函数完全放到密码验证函数的保护之中,所以密码验证函数要接受存款函数。

即  我们这些优化的思路是:使用高阶函数来封装a函数,以来避免对a函数的修改。

def a():

    print("-----存款中-----")

def b(pwd):  # pwd = a

    print("-----密码验证中-----") 

    pwd()     #a()  

b(a)  

这种写法只是没有改变原函数代码,并且增加了新功能。但是他改变了函数的调用方式,这就不符合我们的开放封闭原则。

我们下面进行第二次优化:
这次我们使用闭包来对我们的函数进行优化,这样我们就可以遵循开放封闭原则的前提下,完成功能的增加。这就是一个初级装饰器。

def a():
    print("-----存款中-----")

def  b(pwd):   #pwd=a

    def inner():

        print("-----密码验证中-----")

        pwd()            # pwd()=a()

    return inner          #返回inner函数

a=b(a)              #inner函数返回到这里,即a=inner

a()          #a()=inner(),这里是对inner的调用

    接着我们来细致剖析一下初级装饰器的运行过程。

1.第一次读取:读取a函数的整体。

2.第二次读取  :读取b函数的整体

3.接着开始执行  a = b(a)  将 a函数 带入到 b函数 内,即 def b(a)  此时 在b函数内  pwd=a

4.因为 b函数 接受到了参数,系统开始执行 b函数 :b函数内只做了两件事情  定义了一个

inner函数,将inner函数返回。

5.  a=b(a)=inner  因为 b(a) 返回了 inner,所以 现在在函数外 a=inner

6.接着执行了  a()  调用了 a 函数  此时  因为 a=inner,所以a()=inner()

7.开始执行  inner():  inner()函数干了两件事情 1.print("-----密码验证中-----") 2.pwd()  因为

pwd=a  所以 pwd() = a()

8.开始调用最开始的a函数, 执行 print("-----存款中-----")

9.执行完成。

其实初级装饰器还可以在优化一下:

将初级装饰器的调用语句换成内置调用方式(语法糖)。

def b(pwd):         #这时将a传递给b函数中的pwd 即pwd=a

    def inner():

        print("-----密码验证中-----")

        pwd()

    return inner        #返回inner函数

@b     #这种写法就是语法糖,他就相当于是a=b(a)        #inner函数返回给了a

def a ():

    print("-----存款中-----")

a()         #a函数的调用相当于inner函数的调用

 接着我们来细致剖析一下语法糖装饰器的运行过程。

1.第一次读取 :读取b函数的整体。

2.第二次读取  :   因为读取到了  @b, 所以系统接下来的两行看成整体,即:a = b(a)   def a():

3.系统开始执行  a = b(a)  将 a函数 带入到 b函数 内,即: def b(a)  此时 在b函数内  pwd=a

4.因为 b函数 接受到了参数,系统开始执行 b函数 :  b函数内只做了两件事情  定义了一个 inner函

数,将inner函数返回。

5.  a=b(a)=inner  因为 b(a) 返回了 inner,所以 现在在函数外 a=inner

6.最后执行了  a()  调用了 a函数  此时  因为 a=inner  a()=inner()

7.开始执行  inner():  inner()函数干了两件事情 1.print("-----密码验证中-----") 2.pwd()  因为

pwd=a  所以 pwd() = a()

8.开始调用最开始的a函数, 执行 print("-----存款中-----")

9.执行完成。

装饰器的返回值

现在我们又有了新的需求,我们想要获取存款的过程,而不是将存款过程直接打印出来,这就需要我们的存款函数a有返回值,现在修改如下:

def b(pwd):

    def inner():

        print("-----密码验证中-----")

        r=pwd()         #这里本来是直接调用a函数,现在将他修改为接收a函数的返回值

        return r    #这里将函数a的返回值返回给最后一句a的调用

    return inner   

@b   

def a ():            

    return "-----存款中,存款金额为200-----"      #a函数的返回值

print(a())      #这里是a的调用,同时是a函数返回值的最后一站


带参装饰器

上面是我们普通装饰器,不能够传递参数,灵活性较差,所以就有了我们的带参装饰器,让我们在上面的例子补充一个新功能可以显示我们的存款金额。

def b(pwd):         #这时将a传递给b函数中的pwd 即pwd=a(money)

    def inner(*args,**kwargs):  #让inner使用不定长参数来接收a的参数   *args=(500)

        print("-----密码验证中-----")

        pwd(*args,**kwargs)            #在这里将500赋给pwd,相当于是a(500)

    return inner        #返回inner函数

@b     #这种写法就是语法糖,他就相当于是a=b(a)        #inner函数返回给了a

def a (money):         #此时money=500

    print("-----存款中,存款金额为{}-----".format(money))

a(500)         #a函数的调用相当于inner函数的调用

运行结果为:

-----密码验证中-----

-----存款中,存款金额为500-----

带参语法糖 

现在我们再给他美化一下,在密码验证中之后输出一排*,同时我们又要遵守我们的开放封闭原则,所以要使用带参语法糖,修改如下:

def c(char):               #这时将*传递给c函数的char即char=*

    def b(pwd):         #这时将a传递给b函数中的pwd 即pwd=a

        def inner():

            print("-----密码验证中-----")

            print(char*15)   #char = “*”  输出 ***************

            pwd()                    #pwd = a   这里调用了a函数 输出   -----存款中-----

        return inner              #将inner函数返回给a=b(a)

    return b                         #将b函数返回给a=c(*)

@c("*")            #在这里相当于执行了两条语句 a=c(*)   a=b(a)   这里a接受到了 两个返回值,后一个返回值  innner  覆盖前一个返回值 b  即  a=inner

def a ():

    print("-----存款中-----")

a()         #a函数的调用相当于inner函数的调用

运行结果为:

-----密码验证中-----

***************

-----存款中-----

标准版的装饰器

def wrapper(f):

    def inner(*args,**kwargs):

        f(*args,**kwargs)

    return inner

设计一个装饰器,显示函数运行的时间。 输出格式为(函数名,时间)

import time

def timmer(f):

    def inner ():

        start_time=time.time()

        f()

        end_time=time.time()

        print(f,end_time-start_time)

    return inner

@timmer  fun=timmer(fun)

def fun():

    time.sleep(2)

    print("Welcome to China")

fun()

你可能感兴趣的:(Python-装饰器)