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()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容