无惧面试,带你搞懂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通过opencv实现批量剪切图片
Nov 13 Python
python中使用xlrd读excel使用xlwt写excel的实例代码
Jan 31 Python
Python3.5实现的罗马数字转换成整数功能示例
Feb 25 Python
PyTorch学习:动态图和静态图的例子
Jan 06 Python
python re模块匹配贪婪和非贪婪模式详解
Feb 11 Python
Python爬虫实现模拟点击动态页面
Mar 05 Python
Pycharm 使用 Pipenv 新建的虚拟环境(图文详解)
Apr 16 Python
深入了解Python 变量作用域
Jul 24 Python
python实现模拟器爬取抖音评论数据的示例代码
Jan 06 Python
基于tensorflow __init__、build 和call的使用小结
Feb 26 Python
详解Python openpyxl库的基本应用
Feb 26 Python
python通配符之glob模块的使用详解
Apr 24 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中用foreach来操作数组的代码
2011/07/17 PHP
单点登录 Ucenter示例分析
2013/10/29 PHP
PHP里的单例类写法实例
2015/06/25 PHP
ThinkPHP5&5.1框架关联模型分页操作示例
2019/08/03 PHP
PHP正则表达式函数preg_replace用法实例分析
2020/06/04 PHP
JavaScript 动态改变图片大小
2009/06/11 Javascript
控制页面按钮在后台执行期间不重复提交的JS方法
2013/06/24 Javascript
js日期联动示例
2014/05/02 Javascript
jquery中$(#form :input)与$(#form input)的区别
2014/08/18 Javascript
js实现根据身份证号自动生成出生日期
2015/12/15 Javascript
ichart.js绘制虚线、平均分虚线效果的实现代码
2016/05/05 Javascript
深入理解逻辑表达式的用法 与或非的用法
2016/06/06 Javascript
js获取浏览器的各种属性
2017/04/27 Javascript
详谈javascript精度问题与调整
2017/07/08 Javascript
vue-cli开发时,关于ajax跨域的解决方法(推荐)
2018/02/03 Javascript
Vuex modules模式下mapState/mapMutations的操作实例
2019/10/17 Javascript
JS实现判断移动端PC端功能
2020/02/21 Javascript
跟老齐学Python之总结参数的传递
2014/10/10 Python
Python中线程编程之threading模块的使用详解
2015/06/23 Python
Pycharm学习教程(7)虚拟机VM的配置教程
2017/05/04 Python
通过Python 接口使用OpenCV的方法
2018/04/02 Python
Python操作mongodb数据库进行模糊查询操作示例
2018/06/09 Python
python实现下载pop3邮件保存到本地
2018/06/19 Python
解决pycharm py文件运行后停止按钮变成了灰色的问题
2018/11/29 Python
PyQt 图解Qt Designer工具的使用方法
2019/08/06 Python
python对象销毁实例(垃圾回收)
2020/01/16 Python
pytorch简介
2020/11/11 Python
详解Pymongo常用查询方法总结
2021/01/29 Python
办公室文秘自我鉴定
2013/09/21 职场文书
设计部经理的岗位职责
2013/11/16 职场文书
领导班子四风问题对照检查材料
2014/09/27 职场文书
2015年女工委工作总结
2015/07/27 职场文书
HTML5 新增内容和 API详解
2021/11/17 HTML / CSS
docker-compose部署Yapi的方法
2022/04/08 Servers
python游戏开发Pygame框架
2022/04/22 Python
springboot为异步任务规划自定义线程池的实现
2022/06/14 Java/Android