python装饰器

简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:

1. import time
2. def func():
3.     print("hello")
4.     time.sleep(1)
5.     print("world")

这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,那最简单的做法就是:

1. #原始侵入,篡改原函数
2. import time
3. def func():
4.     startTime = time.time()
5. 
6.     print("hello")
7.     time.sleep(1)
8.     print("world")
9.     endTime = time.time()
10. 
11.     msecs = (endTime - startTime)*1000
12.     print("time is %d ms" %msecs)

但是如果你的Boss在公司里面和你说:这段代码是我们公司的核心代码,你不能直接去改我们的核心代码。”那该怎么办呢,我们仿照装饰器先自己试着写一下:

1. #避免直接侵入原函数修改,但是生效需要再次执行函数
2. import time
3. 
4. def deco(func):
5.     startTime = time.time()
6.     func()
7.     endTime = time.time()
8.     msecs = (endTime - startTime)*1000
9.     print("time is %d ms" %msecs)
10. 
11. 
12. def func():
13.     print("hello")
14.     time.sleep(1)
15.     print("world")
16. 
17. if __name__ == '__main__':
18.     f = func
19.     deco(f)#只有把func()或者f()作为参数执行,新加入功能才会生效
20.     print("f.__name__ is",f.__name__)#f的name就是func()
21.     print()
22.     #func()

这里我们定义了一个函数deco,它的参数是一个函数,然后给这个函数嵌入了计时功能。然后你可以拍着胸脯对老板说,看吧,不用动你原来的代码,我照样拓展了它的函数功能。
然后你的老板有对你说:我们公司核心代码区域有一千万个func()函数,从func01()到func1kw(),按你的方案,想要拓展这一千万个函数功能,就是要执行一千万次deco()函数,这可不行呀,我心疼我的机器。”
好了,你终于受够你老板了,准备辞职了,然后你无意间听到了装饰器这个神器,突然发现能满足你闫博士的要求了。
我们先实现一个最简陋的装饰器,不使用任何语法糖和高级语法,看看装饰器最原始的面貌:

1. #既不需要侵入,也不需要函数重复执行
2. import time
3. 
4. def deco(func):
5.     def wrapper():
6.         startTime = time.time()
7.         func()
8.         endTime = time.time()
9.         msecs = (endTime - startTime)*1000
10.        print("time is %d ms" %msecs)
11.    return wrapper
12.
13. 
14. @deco
15. def func():
16.     print("hello")
17.     time.sleep(1)
18.     print("world")
19. 
20. if __name__ == '__main__':
21.     f = func #这里f被赋值为func,执行f()就是执行func()
22.     f()

这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

1. #带有参数的装饰器
2. import time
3. 
4. def deco(func):
5.     def wrapper(a,b):
6.         startTime = time.time()
7.         func(a,b)
8.         endTime = time.time()
9.         msecs = (endTime - startTime)*1000
10.        print("time is %d ms" %msecs)
11.     return wrapper
12. 
13. 
14. @deco
15. def func(a,b):
16.     print("hello,here is a func for add :")
17.     time.sleep(1)
18.     print("result is %d" %(a+b))
19. 
20. if __name__ == '__main__':
21.     f = func
22.     f(3,4)
23.     #func()

然后你满足了Boss的要求后,Boss又说:我让你拓展的函数好多可是有参数的呀,有的参数还是个数不定的那种,你的装饰器搞的定不?”然后你嘿嘿一笑,深藏功与名!

1. #带有不定参数的装饰器
2. import time
3. 
4. def deco(func):
5.     def wrapper(*args, **kwargs):
6.         startTime = time.time()
7.         func(*args, **kwargs)
8.         endTime = time.time()
9.         msecs = (endTime - startTime)*1000
10.        print("time is %d ms" %msecs)
11.    return wrapper
12. 
13. 
14. @deco
15. def func(a,b):
16.     print("hello,here is a func for add :")
17.     time.sleep(1)
18.     print("result is %d" %(a+b))
19. 
20. @deco
21. def func2(a,b,c):
22.     print("hello,here is a func for add :")
23.     time.sleep(1)
24.     print("result is %d" %(a+b+c))
25. 
26. 
27. if __name__ == '__main__':
28.     f = func
29.     func2(3,4,5)
30.     f(3,4)
31.     #func()

最后,你的老板说:可以的,我这里一个函数需要加入很多功能,一个装饰器怕是搞不定,装饰器能支持多个嘛,最后你就把这段代码丢给了他:

#多个装饰器

import time

def deco01(func):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
        print("deco01 end here")
    return wrapper

def deco02(func):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        func(*args, **kwargs)

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))



if __name__ == '__main__':
    f = func
    f(3,4)
    #func()

'''
this is deco01
this is deco02
hello,here is a func for add :
result is 7
deco02 end here
time is 1003 ms
deco01 end here
'''

多个装饰器执行的顺序就是从第一个装饰器开始,执行到最后一个装饰器,再执行函数本身。

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

推荐阅读更多精彩内容