zoukankan      html  css  js  c++  java
  • python装饰器系列(四)

    带参数的装饰器

    先来看一个不带参数的装饰器

     1 import time
     2 
     3 def timeit(fn):
     4     def wrap(*args,**kwargs):
     5         start = time.time()
     6         ret = fn(*args,**kwargs)
     7         print(time.time() - start)
     8         return ret
     9     return wrap
    10  
    11 
    12 @timeit
    13 def sleep(x):
    14     time.sleep(x)
    1 sleep(3)
    2 3.0034420490264893

    这里打印出来的是执行sleep函数所消耗的自然时间,但在执行此函数时所消耗的cpu时间真的有3.0034420490264893秒吗?当然不是。利用time包中的time.clock方法可以计算得到代码执行所消耗cpu的时间,那怎样来修改上边的timeit函数,让其即能计算代码执行的自然时间,也能计算代码执行所消耗cpu的时间?做如下改进:

     1 def timeit_1(process_time=False):
     2     cacl = time.clock if process_time else time.time
     3     def timeit_2(fn):
     4         def wrap(*args,**kwargs):
     5             start = cacl()
     6             ret = fn(*args,**kwargs)
     7             print(cacl() - start)
     8             return ret
     9         return wrap
    10     return timeit_2
    11 
    12  
    13 def sleep_1(x):
    14     time.sleep(x)
    1 timeit_1(True)(sleep_1)(3)
    2 0.020000000000000018
    1 timeit_1(False)(sleep_1)(3)
    2 3.0038363933563232
    1  # 参数process_time是一个默认参数,所以可以不传递值,默认为False    
    2 timeit_1()(sleep_1)(3)  
    3 3.003509283065796

    调用过程分解:

    1 fn1 = timeit_1(True)

    调用timeit_1(True),函数return回了timeit_2,并把fn1这个变量指向了调用结果,即指向了timeit_2,这里的timeit_2也是一个函数,此函数接收一个参数

    1 fn2 = fn1(sleep_1)

    调用fn1(sleep_1),其实就是调用了timeit_2(sleep_1),并把fn2这个变量指向了调用后的结果,即指向了warp,这里的warp也是一个函数,此函数能接收任意的参数

    1 fn2(3)
    2 0.009999999999999787

    调用fn2(3),其实是调用了wrap(3),即执行了wrap函数内的语句,此函数内的ret = fn(*args,**kwargs)语句中的fn其实是指向了sleep,所以在执行wrap函数时,sleep_1函数才真正被执行

    改进的装饰器装饰一个函数:

    1 @timeit_1(False)
    2 def sleep_2(x):
    3     time.sleep(x)
    1 sleep_2(3)
    2 3.0039477348327637

    计算代码执行的cpu时间

    1 @timeit_1(True)
    2 def sleep_3(x):
    3     time.sleep(x)
    1 sleep_3(3)
    2 0.0

    魔法背后的原理:

    其实质就是在没有用魔法的情况下直接timeit_1(True)(sleep_3)(3)。而当使用@这个魔法后,当代码执行到此行时,解析器会执行timeit_1(True),timeit_1实质就是一函数,接收一个参数,并返回一个timeit_2函数。当代码执行到@所在语句时,会把所装饰的sleep_3函数作为一个参数传递给timeit_1(True)的调用结果,即timeit_2这个函数,即sleep_3这个函数已作为一个变量传递给了timeit_2(fn)中的fn参数,并返回了一个wrap函数,在接下的调用sleep_3(3)这个操作,其实此时的sleep_3这个函数已不是原先的def sleep_3(x):中的sleep_3函数,而是一个指向了wrap的函数,wrap函数接收任何参数,所以把当执行sleep_3(3)时,把参数3传递给了wrap函数,并执行内部的代码,内部代码中ret = fn(*args,**kwargs)中的fn函数依赖还是指向原先的sleep_3(x)这个函数。

    这里也有一个简单的记忆方式,如果一个函数被装饰器所装饰,在调用这个函数时其实不再是调用表面上看上去的这个函数,以来做说明

    1 @timeit_1(True)
    2 def sleep_3(x):
    3     time.sleep(x)

    当执行到有@魔法所在行时,相当于执行了sleep_3 = timeit_1(True)(sleep_3),即指向了wrap函数,既然sleep_3指向了wrap函数,那我们执行sleep_3(3)时,其实就是在进行wrap(3)这样的函数调用,记住,函数名也是一个变量

  • 相关阅读:
    1.4.2.3. SETUP(Core Data 应用程序实践指南)
    1.4.2.2. PATHS(Core Data 应用程序实践指南)
    1.4.2.1. FILES(Core Data 应用程序实践指南)
    1.4.2. 实现 Core Data Helper 类(Core Data 应用程序实践指南)
    1.4.1. Core Data Helper 简介(Core Data 应用程序实践指南)
    1.4. 为现有的应用程序添加 Core Data 支持(Core Data 应用程序实践指南)
    1.3.2. App Icon 和 Launch Image(Core Data 应用程序实践指南)
    1.3.1. 新建Xcode项目并设置故事板(Core Data 应用程序实践指南)
    php验证邮箱是否合法
    如何使js函数异步执行
  • 原文地址:https://www.cnblogs.com/tianshug/p/10921898.html
Copyright © 2011-2022 走看看