初解装饰器

装饰器

装饰器就是一个函数,用来装饰另一个函数。为另一个函数添加新功能。

特点:①不改变原来函数的源代码     ②不改变原来函数的调用方式

装饰器=高阶函数+嵌套函数+函数闭包

加入目前我有一个这样的test函数,想给它增加一个新的功能,就是查看函数的运行时间,那我该怎么做呢?

 

1 import time
2 def test():
3     for i in range(5):
4         time.sleep(0.1)
5     print("运行完毕")
6 
7 test()

 

第一种方法也是最直接的方法:直接改动源代码

 1 import time
 2 def test():
 3     start_time = time.time()
 4     for i in range(5):
 5         time.sleep(0.1)
 6     print("运行完毕")
 7     stop_time = time.time()
 8     print("该函数运行的时间是%s"%(stop_time - start_time))
 9 
10 test()

结果是:

运行完毕
该函数运行的时间是0.5019335746765137

这样改动的话看起来十分简单的操作

然而如果你的手边有10000或者是更多这样的函数,每一个都要这样添加函数功能,那你还会一个一个的去修改这些函数么?

答案是当然不会的啦,这时你就会想到另一个方法,我能不能再另外定义一个函数用来专门统计时间呢?当然可以了

 1 import time
 2 
 3 def timer(func):
 4     start_time = time.time()
 5     func()
 6     stop_time = time.time()
 7     print("该函数运行的时间是%s"%(stop_time - start_time))
 8 
 9 def test():
10     for i in range(5):
11         time.sleep(0.1)
12     print("运行完毕")
13 
14 timer(test)

结果:

运行完毕
该函数运行的时间是0.5027310848236084

这样一看是不是简单多了呢?

但是面你对成千上万的函数,每次都去这样调用是不是还是显得麻烦了呢?

假想一下:你调用函数

timer(test)

timer(test1)

timer(test2)

timer(test3)

.......

这样的话你又突然想到我把test函数的返回值写成return timer,然后定义一个test变量作为test()的返回值,再调用test()不就行了么?这样想一下,貌似是完美的。然而。。。

 1 import time
 2 
 3 def timer():
 4     start_time = time.time()
 5     test()
 6     stop_time = time.time()
 7     print("该函数运行的时间是%s"%(stop_time - start_time))
 8 
 9 def test():
10     for i in range(5):
11         time.sleep(0.1)
12     print("运行完毕")
13     return timer
14 test = test()   #可以使用test()()的方式调用
15 test()

结果:

运行完毕
运行完毕
该函数运行的时间是0.5015039443969727

哇,结果是错的(调用了两次test()函数 ),天啊,而且好像又把它给搞的更加复杂了,那到底应该怎么做呢,真让人头皮发麻!

如果把定义的timer()放到另一个新定义的decorate()中然后decorate函数的返回值设置为timer是不是就可以了呢?来让我们试一下

 1 import time
 2 
 3 def decorate(func):
 4     def timer():
 5         start_time = time.time()
 6         func()
 7         stop_time = time.time()
 8         print("该函数运行的时间是%s"%(stop_time - start_time))
 9     return timer
10 def test():
11     for i in range(5):
12         time.sleep(0.1)
13     print("运行完毕")
14 
15 test = decorate(test)
16 test()

结果:

运行完毕
该函数运行的时间是0.5024490356445312

哇,完美。

然而在python中有一种语法糖就是我们常常用到的@符号

@装饰器函数就等价于(变量 = 装饰器函数(被装饰函数名字))

在这里既是@decorate = decorate(test)

怎么样,是不是很美妙呢?

让我们试一试是不是真的如我所说

 1 import time
 2 
 3 def decorate(func):
 4     def timer():
 5         start_time = time.time()
 6         func()
 7         stop_time = time.time()
 8         print("该函数运行的时间是%s"%(stop_time - start_time))
 9     return timer
10 
11 @decorate
12 
13 def test():
14     for i in range(5):
15         time.sleep(0.1)
16     print("运行完毕")
17     
18 test()

结果:

运行完毕
该函数运行的时间是0.5020065307617188

是了,语法糖@可用无疑

这样一个简单的装饰器就被我们创建出来了

但是新问题又来了,如果我的被装饰函数有返回值或者函数形参怎么办呢?

首先我们先看一下有函数返回值的情况怎么办?

import time

def decorate(func):
    def timer():
        start_time = time.time()
        res = func()     #此处调用被修饰函数,在此处定义一个变量接受被装饰函数的返回值就可以了
        stop_time = time.time()
        print("该函数运行的时间是%s"%(stop_time - start_time))
        return res    #此处返回函数的返回值
    return timer

@decorate

def test():
    for i in range(5):
        time.sleep(0.1)
    return "运行完毕"

print(test())

结果:

该函数运行的时间是0.5053601264953613
运行完毕

看来这样是可以的,我们又解决了一个问题

接下来让我们看看函数有形参的情况下怎么办?

这时候我们就要使用*args, **kwargs这两个可变长参数了,因为我们传入参数数量是不确定的,比如一个函数需要两个参数,另一个需要三个参数,另一个需要4个参数。。。。。

好了,那这样我们该怎么写呢?

 1 import time
 2 
 3 def decorate(func):
 4     def timer(*args, **kwargs):
 5         start_time = time.time()
 6         res = func(*args, **kwargs)
 7         stop_time = time.time()
 8         print("该函数运行的时间是%s"%(stop_time - start_time))
 9         return res
10     return timer
11 
12 @decorate
13 
14 def test(name, age):
15     for i in range(5):
16         time.sleep(0.1)
17     return "我的名字是%s,我的年龄是%s"%(name,age)
18 
19 print(test("bob", 18))

结果:

该函数运行的时间是0.5026912689208984
我的名字是bob,我的年龄是18

这样一个装饰器的基本条件基本上是齐备了 。

最后一个问题,如果我想在每一个被修饰的函数前面天界一句“hello,world!”我该怎么办呢?

简单,就是在装饰函数外在套一个装饰器函数就好了,在这里我套了函数decorate1。

 1 import time
 2 def decorate1(s):
 3     def decorate(func):
 4         def timer(*args, **kwargs):
 5             print("%s"%s)
 6             start_time = time.time()
 7             res = func(*args, **kwargs)
 8             stop_time = time.time()
 9             print("该函数运行的时间是%s"%(stop_time - start_time))
10             return res
11         return timer
12     return decorate
13 @decorate1("hello,world!")
14 
15 def test(name, age):
16     for i in range(5):
17         time.sleep(0.1)
18     return "我的名字是%s,我的年龄是%s"%(name,age)
19 
20 print(test("bob", 18))

结果:

hello,world!
该函数运行的时间是0.5027992725372314
我的名字是bob,我的年龄是18

好了,关于装饰器,目前只能想到这么多了!!!

 

posted @ 2018-07-24 11:44  超级宇宙无敌乖宝宝  阅读(109)  评论(0编辑  收藏  举报