无惧面试,带你搞懂python 装饰器


Posted in Python onAugust 17, 2020

写在之前

「装饰器」作为 Python 高级语言特性中的重要部分,是修改函数的一种超级便捷的方式,适当使用能够有效提高代码的可读性和可维护性,非常的便利灵活。

「装饰器」本质上就是一个函数,这个函数的特点是可以接受其它的函数当作它的参数,并将其替换成一个新的函数(即返回给另一个函数)。

可能现在这么看的话有点懵,为了深入理解「装饰器」的原理,我们首先先要搞明白「什么是函数对象」,「什么是嵌套函数」,「什么是闭包」。关于这三个问题我在很久以前的文章中已经写过了,你只需要点击下面的链接去看就好了,这也是面试中常问的知识哦:

装饰器

搞明白上面的三个问题,其实简单点来说就是告诉你:函数可以赋值给变量,函数可嵌套,函数对象可以作为另一个函数的参数。

首先我们来看一个例子,在这个例子中我们用到了前面列出来的所有知识:

def first(fun):
  def second():
    print('start')
    fun()
    print('end')
    print fun.__name__
  return second

def man():
  print('i am a man()')

f = first(man)
f()

上述代码的执行结果如下所示:

start
i am a man()
end
man

上面的程序中,这个就是 first 函数接收了 man 函数作为参数,并将 man 函数以一个新的函数进行替换。看到这你有没有发现,这个和我在文章刚开始时所说的「装饰器」的描述是一样的。既然这样的话,那我们就把上述的代码改造成符合 Python 装饰器的定义和用法的样子,具体如下所示:

def first(func):
  def second():
    print('start')
    func()
    print('end')
    print (func.__name__)
  return second

@first
def man():
  print('i am a man()')

man()

上面这段代码和之前的代码的作用一模一样。区别在于之前的代码直接“明目张胆”的使用 first 函数去封装 man 函数,而上面这个是用了「语法糖」来封装 man 函数。至于什么是语法糖,不用细去追究,你就知道是类似「@first」这种形式的东西就好了。

在上述代码中「@frist」在 man 函数的上面,表示对 man 函数使用 first 装饰器。「@」 是装饰器的语法,「first」是装饰器的名称。

下面我们再来看一个复杂点的例子,用这个例子我们来更好的理解一下「装饰器」的使用以及它作为 Python 语言高级特性被人津津乐道的部分:

def check_admin(username):
  if username != 'admin':
    raise Exception('This user do not have permission')

class Stack:
  def __init__(self):
    self.item = []

  def push(self,username,item):
    check_admin(username=username)
    self.item.append(item)

  def pop(self,username):
    check_admin(username=username)
    if not self.item:
      raise Exception('NO elem in stack')
    return self.item.pop()

上述实现了一个特殊的栈,特殊在多了检查当前用户是否为 admin 这步判断,如果当前用户不是 admin,则抛出异常。上面的代码中将检查当前用户的身份写成了一个独立的函数 check_admin,在 push 和 pop 中只需要调用这个函数即可。这种方式增强了代码的可读性,减少了代码冗余,希望大家在编程的时候可以具有这种意识。

下面我们来看看上述代码用装饰器来写成的效果:

def check_admin(func):
  def wrapper(*args, **kwargs):
    if kwargs.get('username') != 'admin':
      raise Exception('This user do not have permission')
    return func(*args, **kwargs)
  return wrapper

class Stack:
  def __init__(self):
    self.item = []

  @check_admin
  def push(self,username,item):
    self.item.append(item)

  @check_admin
  def pop(self,username):
    if not self.item:
      raise Exception('NO elem in stack')
    return self.item.pop()

对比一下使用「装饰器」和不使用装饰器的两种写法,乍一看,好像使用「装饰器」以后代码的行数更多了,但是你有没有发现代码看起来好像更容易理解了一些。在没有装饰器的时候,我们先看到的是 check_admin 这个函数,我们得先去想这个函数是干嘛的,然后看到的才是对栈的操作;而使用装饰器的时候,我们上来看到的就是对栈的操作语句,至于 check_admin 完全不会干扰到我们对当前函数的理解,所以使用了装饰器可读性更好了一些。

就和我在之前的文章中所讲的「生成器」那样,虽然 Python 的高级语言特性好用,但也不能乱用。装饰器的语法复杂,通过我们在上面缩写的装饰器就可以看出,它写完以后是很难调试的,并且使用「装饰器」的程序的速度会比不使用装饰器的程序更慢,所以还是要具体场景具体看待。

以上就是无惧面试,带你搞懂python 装饰器的详细内容,更多关于python 装饰器的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
利用Python开发微信支付的注意事项
Aug 19 Python
Python实现SSH远程登陆,并执行命令的方法(分享)
May 08 Python
Python的地形三维可视化Matplotlib和gdal使用实例
Dec 09 Python
DataFrame 将某列数据转为数组的方法
Apr 13 Python
Python实现迭代时使用索引的方法示例
Jun 05 Python
pycharm 实现显示project 选项卡的方法
Jan 17 Python
python matplotlib库绘制散点图例题解析
Aug 10 Python
PYTHON绘制雷达图代码实例
Oct 15 Python
Python下应用opencv 实现人脸检测功能
Oct 24 Python
python3实现用turtle模块画一棵随机樱花树
Nov 21 Python
利用pyshp包给shapefile文件添加字段的实例
Dec 06 Python
Python之Matplotlib绘制热力图和面积图
Apr 13 Python
Python Request类源码实现方法及原理解析
Aug 17 #Python
浅谈Python 钉钉报警必备知识系统讲解
Aug 17 #Python
Python钉钉报警及Zabbix集成钉钉报警的示例代码
Aug 17 #Python
Django DRF认证组件流程实现原理详解
Aug 17 #Python
python使用建议与技巧分享(二)
Aug 17 #Python
详解python方法之绑定方法与非绑定方法
Aug 17 #Python
如何利用python之wxpy模块玩转微信
Aug 17 #Python
You might like
php mysql Errcode: 28 终极解决方法
2009/07/01 PHP
php通过array_push()函数添加多个变量到数组末尾的方法
2015/03/18 PHP
PHP调用存储过程返回值不一致问题的解决方法分析
2016/04/26 PHP
关于Anemometer图形化显示MySQL慢日志的工具搭建及使用的详细介绍
2020/07/13 PHP
xml 封装与解析(javascript和C#中)
2009/07/26 Javascript
通过jQuery源码学习javascript(一)
2012/12/27 Javascript
jquery乱码与contentType属性设置问题解决方案
2013/01/07 Javascript
JavaScript获取客户端计算机硬件及系统等信息的方法
2014/01/02 Javascript
学习JavaScript设计模式之中介者模式
2016/01/14 Javascript
Bootstrap嵌入jqGrid,使你的table牛逼起来
2016/05/05 Javascript
js removeChild 方法深入理解
2016/08/16 Javascript
vue使用mint-ui实现下拉刷新和无限滚动的示例代码
2017/11/06 Javascript
jQuery插件Validation表单验证详解
2018/05/26 jQuery
jQuery实现导航样式布局操作示例【可自定义样式布局】
2018/07/24 jQuery
浅析Vue项目中使用keep-Alive步骤
2018/07/27 Javascript
vuex 动态注册方法 registerModule的实现
2019/07/03 Javascript
html+jQuery实现拖动滑块图片拼图验证码插件【移动端适用】
2019/09/10 jQuery
详解node登录接口之密码错误限制次数(含代码)
2019/10/25 Javascript
在Python3中使用asyncio库进行快速数据抓取的教程
2015/04/02 Python
python爬虫实战之最简单的网页爬虫教程
2017/08/13 Python
Python语言实现百度语音识别API的使用实例
2017/12/13 Python
python实现京东订单推送到测试环境,提供便利操作示例
2019/08/09 Python
python Event事件、进程池与线程池、协程解析
2019/10/25 Python
Tensorflow的梯度异步更新示例
2020/01/23 Python
Pytest参数化parametrize使用代码实例
2020/02/22 Python
python 实现压缩和解压缩的示例
2020/09/22 Python
CSS 说明横向进度条最后显示文字的实现代码
2020/11/10 HTML / CSS
购买澳大利亚最好的服装和内衣在线:BONDS
2016/10/14 全球购物
中国最大的潮流商品购物网站:YOHO!BUY有货
2017/01/07 全球购物
Ever New美国:澳大利亚领先的女装时尚品牌
2019/11/28 全球购物
机关单位人员学雷锋心得体会
2014/03/10 职场文书
销售经理岗位职责
2014/03/16 职场文书
环保倡议书格式范文
2014/05/14 职场文书
2014年个人师德工作总结
2014/12/04 职场文书
青年文明号创建口号大全
2015/12/25 职场文书
Springboot/Springcloud项目集成redis进行存取的过程解析
2021/12/04 Redis