zoukankan      html  css  js  c++  java
  • 初解装饰器

    装饰器

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

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

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

    加入目前我有一个这样的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

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

  • 相关阅读:
    非类型模板参数(针对C++)
    继承(针对C++)
    进程中的线程共享的资源有哪些?
    关键字typename(针对C++)
    设计模式之——工厂模式
    利用多线程同步互斥来创建自己的资源锁
    SQL优化总结
    委托与泛型
    第5章 事件和数据回发机制
    jQuery权威指南_读书笔记
  • 原文地址:https://www.cnblogs.com/hexiaoqi/p/9359118.html
Copyright © 2011-2022 走看看