python装饰器(fuctional decorators)简单来说就是修改其他函数的函数。

这样的函数需要满足两个个条件:

1、不能修改原函数的源代码

2、不能改变原函数的调用方式

需要达到的效果:增加函数的功能

假设,我们已经定义了一个函数

import time

def test():

  time.sleep(2)

  print('The is test')

现在需要为这个程序添加一个功能:显示该程序运行的时间

一、预备知识

理解装饰器的难点在于:

1、理解python一切皆为对象的含义

2、高阶函数

3、嵌套函数

1、一切皆对象

这里只是简单看一下,理解函数即变量即可。后面学习深入会有更深入的理解。

  1. >>> def say(name):
  2. ... return 'hello '+ name
  3. ...
  4. >>> print(say())
  5. >>> print(say('Michael'))
  6. hello Michael
  7. >>>
  8. # 我们可以将函数名作为变量赋值给另一个变量,换句话说变量可以指向函数
  9. >>> greet=say
  10. >>> print(greet())
  11. >>> print(greet('Bob'))
  12. hello Bob
  13. # 变量删除,对象也就不存在了
  14. >>> del say
  15. >>> say('Jack')
  16. Traceback (most recent call last):
  17. File "<stdin>", line 1, in <module>
  18. NameError: name 'say' is not defined
  19. >>>

2、高阶函数

高阶函数有两类:
a.把一个函数名当作实参传给另外一个函数
b.返回值中包含函数名

python一切皆为对象,实际上函数名就是函数的地址,也可以认为,是函数的一个标签。这样就可以把函数名作为一个参数传给另一个函数,在另一个函数里做一些操作,就可以在不修改源代码的基础上,为函数增加一些功能。

  1. import time
  2. def test():
  3. print('This is test')
  4.  
  5. def deco(func):
  6. start_t = time.time()
  7. func()
  8. end_t = time.time()
  9. print('the func %s run time is %s' % (func, end_t-start_t))
  10.  
  11. deco(test)

在第11行处,把test作为实参传给形参func,在第7行处就是对形参的调用。func传入的是函数的地址,func()就是执行这个函数,这段代码运行的结果就是:

  1. This is test
  2. the func <function test at 0x00000195C816EAE8> run time is 0.0

那么再思考,如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名

  1. def test():
  2. time.sleep(3)
  3. print('This is test')
  4.  
  5. def deco(func):
  6. print('This is deco')
  7. return func
  8.  
  9. test = deco(test)
  10. test()

这种方式虽然可以不改变调用方式,但是无法完成计时功能,运算结果

  1. This is deco
  2. This is test

3、嵌套函数

  1. import time
  2. def timer(func):
  3. def deco():
  4. start = time.time()
  5. func()
  6. stop = time.time()
  7. print('The func running time is %s' % (stop - start))
  8. return deco
  9.  
  10. def test():
  11. time.sleep(2)
  12. print("This is test")
  13.  
  14. test = timer(test)
  15. test()

在第14行处,把test作为参数传递给了timer(),此时,在timer()内部,func = test,接下来,定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。

然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数体的标签换掉了,换成了deco。那么在第15行处调用的实际上是deco()。执行结果

  1. This is test
  2. The func running time is 2.000332832336426

那么通俗一点的理解就是:

把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)。

二、真正的装饰器

根据以上分析,装饰器在装饰时,需要在每个函数前面加上:

  1. test = timer(test)

显然有些麻烦,Python提供了一种语法糖,即:

@timer

这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。

以上为无参形式。

  1. import time
  2.  
  3. def decorator(func):
  4. def wapper():#若被装饰函数含参,传到wapper即可
  5. start_t = time.time()
  6. func()
  7. stop_t = time.time()
  8. print('the func running time is %s' % (stop_t-start_t))
  9. return wapper
  10.  
  11. @decorator #test1=decorator(test1) =wapper大盒子替换到中盒子
  12. def test():#表面是小盒子,实际上是中盒子
  13. time.sleep(3)
  14. print('in the test1')
  1. 执行结果:
    in the test1
  2. the func running time is 3.0000483989715576

含参的装饰器

对于一个实际问题,往往是有参数的,如果要在#8处,给被修饰函数加上参数,显然这段程序会报错的。错误原因是test()在调用的时候缺少了一个位置参数的。而我们知道test = func = deco,因此test()=func()=deco() ,那么当test(parameter)有参数时,就必须给func()和deco()也加上参数,为了使程序更加有扩展性,因此在装饰器中的deco()和func(),加如了可变参数*agrs和 **kwargs。

  1. improt time
  2.  
  3. def timer(func)
  4. def deco(*args, **kwargs):
  5. start = time.time()
  6. func(*args, **kwargs)
  7. stop = time.time()
  8. print(stop-start)
  9. return deco
  10.  
  11. @timer
  12. def test(parameter):
  13. time.sleep(2)
  14. print("test is running!")
  15. test()

带返回值的装饰器

test()返回值返回到deco()的内部,而不是test()即deco()的返回值,那么就需要再返回func()的值,因此就是:

  1. def timer(func)
  2. def deco(*args, **kwargs):
  3. start = time.time()
  4. res = func(*args, **kwargs)#
  5. stop = time.time()
  6. print(stop-start)
  7. return res#
  8. return deco

一个较为完整的装饰器

  1. improt time
  2.  
  3. def timer(func)
  4. def deco(*args, **kwargs): #含参的test()函数
  5. start = time.time()
  6. res = func(*args, **kwargs)
  7. stop = time.time()
  8. print(stop-start)
  9. return res #函数带有返回值
  10. return deco
  11.  
  12. @timer
  13. def test(parameter):
  14. time.sleep(2)
  15. print("test is running!")
  16. return "Returned value"
  17. test()

更复杂的装饰器

又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:

  1. @decorator(parameter = value)

比如两个函数

  1. def task1():
  2. time.sleep(2)
  3. print("in the task1")
  4.  
  5. def task2():
  6. time.sleep(2)
  7. print("in the task2")
  8.  
  9. task1()
  10. task2()

要对这两个函数分别统计运行时间,但是要求统计之后输出:

  1. the task1/task2 run time is : 2.00……

于是就要构造一个装饰器timer,并且需要告诉装饰器哪个是task1,哪个是task2,也就是要这样:

  1. @timer(parameter='task1') #
  2. def task1():
  3. time.sleep(2)
  4. print("in the task1")
  5.  
  6. @timer(parameter='task2') #
  7. def task2():
  8. time.sleep(2)
  9. print("in the task2")
  10.  
  11. task1()
  12. task2()

那么方法有了,但是我们需要考虑如何把这个parameter参数传递到装饰器中,我们以往的装饰器,都是传递函数名字进去,而这次,多了一个参数,要怎么做呢? 
于是,就想到再加一层函数来接受参数,根据嵌套函数的概念,要想执行内函数,就要先执行外函数,才能调用到内函数,那么就有:

  1. def timer(parameter): #
  2. print("in the auth :", parameter)
  3.  
  4. def outer_deco(func): #
  5. print("in the outer_wrapper:", parameter)
  6.  
  7. def deco(*args, **kwargs):
  8.  
  9. return deco
  10.  
  11. return outer_deco

首先timer(parameter),接收参数parameter=’task1/2’,而@timer(parameter)也恰巧带了括号,那么就会执行这个函数, 那么就是相当于:

  1. timer = timer(parameter)
  2. task1 = timer(task1)

完整的实现

  1. import time
  2. def timer(parameter):
  3. def outer_wrapper(func):
  4. def wrapper(*args, **kwargs):
  5. if parameter == 'task1':
  6. start = time.time()
  7. func(*args, **kwargs)
  8. stop = time.time()
  9. print("the task1 run time is :", stop - start)
  10. elif parameter == 'task2':
  11. start = time.time()
  12. func(*args, **kwargs)
  13. stop = time.time()
  14. print("the task2 run time is :", stop - start)
  15.  
  16. return wrapper
  17.  
  18. return outer_wrapper
  19. @timer(parameter='task1')#1 timer = timer(parameter)此时timer=out_wrapper 2 task1 = timer(task1)此时相当于task1=wrapper(task1)
  1. def task1():
  2.  
  3. time.sleep(2)
  4.  
  5. print("in the task1")
  6.  
  7. @timer(parameter='task2')
    def task2():
  8.  
  9. time.sleep(2)

执行结果

  1. in the task1
  2. the task1 run time is : 2.000471591949463
  3. in the task2
  4. the task2 run time is : 2.000399589538574

python函数装饰器详解的更多相关文章

  1. python装饰器1:函数装饰器详解

    装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 先混个眼熟 谁可以作为装饰器(可以将谁编写成装饰器): 函数 方法 实现了__call__的可调用类 装饰器可以去装饰谁(谁可以被装饰): 函 ...

  2. python之装饰器详解

    这几天翻看python语法,看到装饰器这里着实卡了一阵,最初认为也就是个函数指针的用法,但仔细研究后发现,不止这么简单. 首先很多资料将装饰器定义为AOP的范畴,也就是Aspect Oriented ...

  3. python 叠加装饰器详解

    def out1(func1): #7.func1=in2的内存地址,就是in2 print('out1') def in1(): #8.调用函数index() 因为函数在in1里,所以首先运行in1 ...

  4. 【python】装饰器详解推荐

    复杂道理浅中来,见过的解释最清楚的一篇文章: https://blog.csdn.net/xiangxianghehe/article/details/77170585

  5. Python函数装饰器原理与用法详解《摘》

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  6. python设计模式之装饰器详解(三)

    python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...

  7. python函数-装饰器

    python函数-装饰器 1.装饰器的原则--开放封闭原则 开放:对于添加新功能是开放的 封闭:对于修改原功能是封闭的 2.装饰器的作用 在不更改原函数调用方式的前提下对原函数添加新功能 3.装饰器的 ...

  8. Python函数装饰器高级用法

    在了解了Python函数装饰器基础知识和闭包之后,开始正式学习函数装饰器. 典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过 ...

  9. Python 函数装饰器

    首次接触到装饰器的概念,太菜啦! Python 装饰器可以大大节省代码的编写量,提升代码的重复使用率.函数装饰器其本质也是一个函数,我们可以把它理解为函数中定义了一个子函数. 例如我们有这么一个需求, ...

随机推荐

  1. day64--pymysql模块的使用、视图、触发器、函数、存储过程、事务

    一.pymysql的下载和使用 (一)pymysql模块的下载:pip3 install pymysql # 实现:使用Python实现用户登录,如果用户存在则登录成功(假设该用户已在数据库中) im ...

  2. Windows程序设计--(四)文本输出

    4.1 绘制和重绘 4.1.2 有效矩阵和无效矩阵 在擦除对话框之后,需要重画的被对话框遮住的矩形区域,这个区域称为「无效区域」或「更新区域」.正是显示区域内无效区域的存在,才会让Windows将一个 ...

  3. 【学习总结】GirlsInAI ML-diary day-20-初识 Kaggle

    [学习总结]GirlsInAI ML-diary 总 原博github链接-day20 初识kaggle 1-注册一个账号(由于被谷歌收购,因此可能需要梯子) 2-Competition - 学会看一 ...

  4. 330-支持PXIE带FMC接口的Xilinx FPGA XC7K325T PCIeX8 接口卡平台

    支持PXIE带FMC接口的Xilinx FPGA XC7K325T PCIeX8 接口卡平台 一.板卡概述     本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片,pin_ ...

  5. java 接口默认方法的使用

  6. day20 python异常处理 try except

    day20 python   一.异常处理     1.什么是异常, 常见异常有:         逻辑错误 ''' name Traceback (most recent call last):   ...

  7. Synchronized和ReentranLock的比较

    并发编程最容易遇到的问题就是就是安全问题,因此解决方式有两种 使用同步方法或同步代码块(Synchronized关键字) 使用锁机制(ReentranLock) 同步方法和同步代码块(Synchron ...

  8. 如何解决拖拽或者缩放、移动中的组件canvas有残留情况

    当我们在做某些需求,如要拖动echarts图表,或者放大缩小 这个时候,有时连续操作,或者在ie或者内存只有8G的电脑上就会出现canvs残留的情况 我们移动的时候,使用的是transform去做的移 ...

  9. Spring Transaction Isolation

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11530702.html Reference http://docs.spring.io/spring/ ...

  10. boost exception

    boost exception provides a new exception type, that lets you add data to an exception after it has b ...