一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了。转载请注明出处http://www.cnblogs.com/Wxtrkbc/p/5486253.html

一、装饰器

  装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),装饰器的功能非常强大,但是理解起来有些困难,因此我尽量用最简单的例子一步步的说明这个原理。

1、不带参数的装饰器

  假设我定义了一个函数f,想要在不改变原来函数定义的情况下,在函数运行前打印出start,函数运行后打印出end,要实现这样一个功能该怎么实现?看下面如何用一个简单的装饰器来实现:

  1. # 使用@语法放在函数的定义上面 相当于执行 f=outer(f),此时f赋值成为了一个新的outer函数,
  2. # 此时f函数就指向了outer函数的返回值inner,inner是一个函数名,定义在oute函数里面
  3. # 原来的f是函数名可简单理解为一个变量,作为outer函数的参数传递进去了 此时参数func相当于f
  4. def outer(func): # 定义一个outer函数作为装饰器
  5. def inner():            # 如果执行inner()函数的话步骤如下:
  6. print('start') # 1、首先打印了字符‘start’,
  7. r=func() # 2、执行func函数,func函数相当于def f(): print('中')
  8. print('end') # 3、接着函数打印‘end’
  9. return r # 4、将func函数的结果返回
  10. return inner
  11.  
  12. @outer
  13. def f(): # f=outer(f)=innner
  14. print('中')
  15.  
  16. f() # f()相当于inner(),执行inner函数的步骤看上面定义处的注释
    #打印结果顺序为 start 中 end

2、包含任意参数的装饰器

  在实际中,我们的装饰器可能应用到不同的函数中去,这些函数的参数都不一样,那么我们怎么实现一个对任意参数都能实现功能的装饰器?还记得我写函数那篇博客中,就写一种可以接受任意参数的函数,下面来看看如何将其应用到装饰器中去  

  1. #其实只要将上面一种不带参数的装饰器修改一下就可以了
  2. #修改也很简单,只需将inner和func的参数改为 (*args,**kwargs)
  3. #其他实现的过程和上面一种一样,就不再介绍了
  4. def outer(func):
  5. def inner(*args,**kwargs):
  6. print('start')
  7. r=func(*args,**kwargs) # 这里func(*args,**kwargs)相当于f(a,b)
  8. print('end')
  9. return r
  10. return inner
  11.  
  12. @outer
  13. def f(a,b):
  14. print(a+b)
  15. f(1,4)           # f(1,4)相当于inner(1,4) 这里打印的结果为 start 5 end

3、使用两个装饰器

  当一个装饰器不够用的话,我们就可以用两个装饰器,当然理解起来也就更复杂了,当使用两个装饰器的话,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解使用@语法的时候到底执行了什么,是理解装饰器的关键。这里还是用最简单的例子来进行说明。  

  1. def outer2(func2):
  2. def inner2(*args,**kwargs):
  3. print('开始')
  4. r=func2(*args,**kwargs)
  5. print('结束')
  6. return r
  7. return inner2
  8.  
  9. def outer1(func1):
  10. def inner1(*args,**kwargs):
  11. print('start')
  12. r=func1(*args,**kwargs)
  13. print('end')
  14. return r
  15. return inner1
  16.  
  17. @outer2 # 这里相当于执行了 f=outer1(f) f=outer2(f),步骤如下
  18. @outer1 #1、f=outer1(f) f被重新赋值为outer1(1)的返回值inner1,
  19. def f(): # 此时func1为 f():print('f 函数')
  20. print('f 函数') #2、f=outer2(f) 类似f=outer2(inner1) f被重新赋值为outer2的返回值inner2
  21. # 此时func2 为inner1函数 inner1里面func1函数为原来的 f():print('f 函数')
  22.  
  23. f() # 相当于执行 outer2(inner1)()
  24. >>开始 # 在outer函数里面执行,首先打印 ‘开始 ’
  25. >>start # 执行func2 即执行inner1函数 打印 ‘start’
  26. >>f 函数 # 在inner1函数里面执行 func1 即f()函数,打印 ‘f 函数’
  27. >>end # f函数执行完,接着执行inner1函数里面的 print('end')
  28. >>结束 # 最后执行inner2函数里面的 print('结束')

4、带参数的装饰器  

  前面的装饰器本身没有带参数,如果要写一个带参数的装饰器怎么办,那么我们就需要写一个三层的装饰器,而且前面写的装饰器都不太规范,下面来写一个比较规范带参数的装饰器,下面来看一下代码,大家可以将下面的代码自我运行一下

  1. import functools
  2.  
  3. def log(k=''):   #这里参数定义的是一个默认参数,如果没有传入参数,默认为空,可以换成其他类型的参数
  4. def decorator(func):
  5. @functools.wraps(func)   #这一句的功能是使被装饰器装饰的函数的函数名不被改变,
  6. def wrapper(*args, **kwargs):
  7. print('start')
  8. print('{}:{}'.format(k, func.__name__)) #这里使用了装饰器的参数k
  9. r = func(*args, **kwargs)
  10. print('end')
  11. return r
  12. return wrapper
  13. return decorator
  14.  
  15. @log() # fun1=log()(fun1) 装饰器没有使用参数
  16. def fun1(a):
  17. print(a + 10)
  18.  
  19. fun1(10)
  20. # print(fun1.__name__) # 上面装饰器如果没有@functools.wraps(func)一句的话,这里打印出的函数名为wrapper
  21.  
  22. @log('excute') # fun2=log('excute')(fun2) 装饰器使用给定参数
  23. def fun2(a):
  24. print(a + 20)
  25. fun2(10)

  

  

Python全栈开发之8、装饰器详解的更多相关文章

  1. 战争热诚的python全栈开发之路

    从学习python开始,一直是自己摸索,但是时间不等人啊,所以自己为了节省时间,决定报个班系统学习,下面整理的文章都是自己学习后,认为重要的需要弄懂的知识点,做出链接,一方面是为了自己找的话方便,一方 ...

  2. python全栈开发之OS模块的总结

    OS模块 1. os.name()      获取当前的系统 2.os.getcwd      #获取当前的工作目录 import os cwd=os.getcwd() # dir=os.listdi ...

  3. Python全栈开发之4、迭代器、生成器、装饰器

    一.迭代器 1.为何要有迭代器? 对于序列类型:字符串.列表.元组,我们可以使用索引的方式迭代取出其包含的元素.但对于字典.集合.文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依 ...

  4. python全栈开发之路

    一.Python基础 python简介 python数据类型(数字\字符串\列表) python数据类型(元组\字典) python数据类型(集合) python占位符%s,%d,%r,%f prin ...

  5. Python全栈开发之3、深浅拷贝、变量和函数、递归、函数式编程、内置函数

    一.深浅拷贝 1.数字和字符串 对于 数字 和 字符串 而言,赋值.浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址. import copy # 定义变量 数字.字符串 # n1 = 123 n1 ...

  6. Python全栈开发之MySQL(二)------navicate和python操作MySQL

    一:Navicate的安装 1.什么是navicate? Navicat是一套快速.可靠并价格相宜的数据库管理工具,专为简化数据库的管理及降低系统管理成本而设.它的设计符合数据库管理员.开发人员及中小 ...

  7. python 全栈开发,Day114(装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL)

    一.装饰器 装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象. 装饰器的应用场景:比如插入日志,性能测试,事务处理, ...

  8. Python全栈开发之14、Javascript

    一.简介 前面我们学习了html和css,但是我们写的网页不能动起来,如果我们需要网页出现各种效果,那么我们就要学习一门新的语言了,那就是JavaScript,JavaScript是世界上最流行的脚本 ...

  9. Python全栈开发之1、输入输出与流程控制

    Python简介 python是吉多·范罗苏姆发明的一种面向对象的脚本语言,可能有些人不知道面向对象和脚本具体是什么意思,但是对于一个初学者来说,现在并不需要明白.大家都知道,当下全栈工程师的概念很火 ...

随机推荐

  1. [vim]乱码问题

    在vim输入中文乱码 1. 检查系统是否支持中文 locale -a 没有中文支持 安装中文包 apt-get install language-pack-zh-hans -y 2.这样可以输入中文了 ...

  2. 在服务器上运行Jar包

    在服务器上运行Jar包 并且该Jar包依赖其他的Jar文件的时候,采用如下格式 java -Djava.ext.dirs=你依赖的Jar文件路径 -jar 你要运行的Jar文件 包名+类名 例如: j ...

  3. sso单点登录的PHP实现(Laravel框架)

    简单说一下我的逻辑,我也不知道我理解sso对不对. 假如三个站点 a.baidu.com b.baidu.com c.baidu.com a.baidu.com 作为验证用户登录账户. b和c作为客户 ...

  4. Codechef Dynamic Trees and Queries

    Home » Practice(Hard) » Dynamic Trees and Queries Problem Code: ANUDTQSubmit https://www.codechef.co ...

  5. Linux网络知识

    在思科上面模拟一下数据包的传递过程:一般上网使用的协议是tcp 交换机是一个2层的设备,它和Ip地址是没有关系的. 交换机上主要处理的是硬件地址(MAC),它只能分析到硬件地址,再到IP地址它就不管了 ...

  6. 【BZOJ】1485: [HNOI2009]有趣的数列

    [算法]Catalan数 [题解] 学了卡特兰数就会啦>_<! 因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一. 那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢. 将做偶数标为 ...

  7. 03.WebView演练-iOS开发Demo(示例程序)源代码

    技术博客http://www.cnblogs.com/ChenYilong/   新浪微博http://weibo.com/luohanchenyilong   //转载请注明出处--本文永久链接:h ...

  8. Problem 2278 YYS (FZU + java大数)

    题目链接:http://acm.fzu.edu.cn/problem.php?pid=2278 题目: 题意: 有n种卡牌,每种卡牌被抽到的概率为1/n,求收齐所有卡牌的天数的期望. 思路: 易推得公 ...

  9. Price(洛谷P4109 [HEOI2015]定价)

    题目 思路: 按照我的思路这一题应该是这样子的 剔除+判断 剔除 因为后面的0要越多越好,所以我们判断0出现的情况,当2个数之间的差大与10时,证明2个之间会存在一个0,所以这一位我们可以把它去掉,相 ...

  10. 41、用Python实现一个二分查找的函数

    data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] def binary_search(dataset ...