Python装饰器使用你可能不知道的几种姿势


Posted in Python onOctober 25, 2019

前言

在Python中,装饰器是一种十分强大并且好用的语法,一些重复的代码使用装饰器语法的话能够使代码更容易理解及阅读。

因此在这里简单总结了一下Python中装饰器的几种用法以及需要注意的事情。

一、在装饰器中获取被装饰函数的参数

假设我们在开发web的时候,需要做反爬。要判断接口的访问来源我们就可以通过下面装饰器的方法来实现:

def mydecorator(func):
 def wrapped(*args, **kwargs):
  print("进入装饰器")
  if args[0]['header'] == 'spider':
   print("code: 400")
   return

  result = func(*args, **kwargs)
  return result

 return wrapped
@mydecorator
def request_page(request):
 print("一个访问请求")
 print("返回了response")
if __name__ == '__main__':
 request = {
  'data': 100,
  'header': 'spider'
 }
 request_page(request)

在这个装饰器中,我们在装饰器中获取了request中的header参数,如果判断访问来源于爬虫,那么便给它返回一个400。
使用装饰器的写法等同于下面不使用装饰器的写法

def mydecorator(*args, **kwargs):
 print("进入函数")
 if args[0]['header'] == 'spider':
  print("code: 400")
  return False
 return True
def request_page(request):
 if not mydecorator(request):
  return
 print("访问一个网页")
 print("得到了response")
if __name__ == '__main__':
 request = {
  'data': 100,
  'header': 'spider'
 }
 request_page(request)

在只需要装饰一个函数的时候后面一种写法可能更优于装饰器的写法,但是在需要装饰很多个函数的时候,使用装饰器明显是更好的选择。

二、在装饰器获取函数的返回值

有的时候我们需要对函数的返回值做出判断,但又不想直接将判断写在函数里的时候,我们也可以使用装饰器来实现:

def mydecorator(func):
 def wrapped(*args, **kwargs):
  print("进入装饰器")
  result = func(*args, **kwargs)
  if result == 400:
   print("response is 400!")
   return False
  return True
 return wrapped

@mydecorator
def request_page():
 print("访问一个网页")
 print("得到了response")
 return 200

if __name__ == '__main__':
 print(request_page())

三、给装饰器传入参数

在实际应用中,我们有时需要根据函数的执行状态来重复执行。例如在编写爬虫的时候,可能由于网络的原因会导致一些页面访问失败,这时我们就需要根据爬虫的返回结果进行重复请求。

def retry(MAXRETRY=3):
 def decorator(func):
  def wrapped(*args, **kwargs):
   print("进入装饰器")

   result = 0
   retry = 1
   while result != 200 and retry <= MAXRETRY:
    result = func(*args, **kwargs)
    print("重试第%s次" % retry)
    retry += 1

   return result

  return wrapped

 return decorator
 
@retry(5)
def request_page():
 print("访问一个网页")
 print("得到了response")
 return 400

在这里我们假设访问一个网页得到400的时候便重新请求。我们在retry装饰器里传了一个5,这表示我们希望重试的最大次数为5次,如果不传入这个值,那么它的默认重试次数则为3次。

在熟悉了基本装饰器的写法后,传参装饰器的写法也十分的好理解了。就是在外面多加了一层函数,用于传入参数。

四、装饰器文档的问题

我们都知道通过魔术方法__doc__可以获取我们写在代码中的文档,那么你是否知道使用装饰器后,会造成被包装函数的文档被装饰器的文档覆盖的问题呢。

def request_page():
 '''
 request_page 函数文档
 :return:
 '''
 print("访问一个网页")
 print("得到了response")

if __name__ == '__main__':
 print(request_page.__doc__)

在上面对上面未使用装饰的代码使用__doc__方法的时候,我们得到的结果是:

In[3]: request_page.__doc__
Out[3]: '\n  request_page 函数文档\n  :return:\n  '

这是我们理想中的结果!

但是当我们将上述函数使用装饰器装饰后:

def decorator(func):
  def wrapped(*args, **kwargs):
    '''
    装饰器文档
    :param args:
    :param kwargs:
    :return:
    '''
    print("进入装饰器")
    result = func(*args, **kwargs)
    return result

  return wrapped


@decorator
def request_page():
  '''
  request_page 函数文档
  :return:
  '''
  print("访问一个网页")
  print("得到了response")

我们再一次运行__doc__魔术方法的时候,得到的结果却是装饰器的内部文档:

In[4]: request_page.__doc__
Out[4]: '\n    装饰器文档\n    :param args:\n    :param kwargs:\n    :return:\n    '
In[5]: request_page.__name__
Out[5]: 'wrapped'

这个问题会使得我们的调试变得困难,也会使许多自动文档生成工具失去效果。

解决这个问题的最好办法就是使用 functools包的wraps()模块来将装饰器进行一个包装。

from functools import wraps
def decorator(func):

  @wraps(func)
  def wrapped(*args, **kwargs):
    '''
    装饰器
    :param args:
    :param kwargs:
    :return:
    '''
    print("进入装饰器")
    result = func(*args, **kwargs)
    return result
  return wrapped

@decorator
def request_page():
  '''
  request_page 函数文档
  :return:
  '''
  print("访问一个网页")
  print("得到了response")

使用wraps将装饰器装饰后,这样我们的函数便能够保存它的一些重要数据了。

In[3]: request_page.__doc__
Out[3]: '\n  request_page 函数文档\n  :return:\n  '
In[3]: request_page.__name__
Out[4]: 'request_page'

五、使用class的写法来编写装饰器

虽然大多数的装饰器都是通过函数的写法来实现的,但同样的可以通过类的写法来实现装饰器。

使用类的写法,我们可以实现一些使用函数写法不太好实现的需求。例如记录一个函数执行的次数

class Decorator():
  def __init__(self,func):
    print('类初始化')
    self.func = func
    self.count = 0
  def __call__(self, *args, **kwargs):
    print('进入装饰器')
    result = self.func(*args,**kwargs)
    self.count += 1

    return result
@Decorator
def request_page():
  '''
  request_page
  :return:
  '''
  print("访问一个网页")
  print("得到了response")

六、总结

装饰器是Python里比较高级的一种语法,这里只是介绍了它的几种使用技巧,以及需要注意的问题。借用金庸先生的话,“武功无高低,修为有深浅”。想要更加灵活的使用装饰器,深入理解它的原理,我们在平时还是需要加强基本功的学习!

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python脚本实现分析dns日志并对受访域名排行
Sep 18 Python
pip命令无法使用的解决方法
Jun 12 Python
python编写简易聊天室实现局域网内聊天功能
Jul 28 Python
django利用request id便于定位及给日志加上request_id
Aug 26 Python
使用python实现快速搭建简易的FTP服务器
Sep 12 Python
python pyheatmap包绘制热力图
Nov 09 Python
python 检查是否为中文字符串的方法
Dec 28 Python
python实现最小二乘法线性拟合
Jul 19 Python
python自动保存百度盘资源到百度盘中的实例代码
Aug 26 Python
Django 自定义404 500等错误页面的实现
Mar 08 Python
使用python实现时间序列白噪声检验方式
Jun 03 Python
Python+腾讯云服务器实现每日自动健康打卡
Dec 06 Python
win7下 python3.6 安装opencv 和 opencv-contrib-python解决 cv2.xfeatures2d.SIFT_create() 的问题
Oct 24 #Python
Python下应用opencv 实现人脸检测功能
Oct 24 #Python
Python迭代器iterator生成器generator使用解析
Oct 24 #Python
Python 取numpy数组的某几行某几列方法
Oct 24 #Python
Django和Flask框架优缺点对比
Oct 24 #Python
python命令 -u参数用法解析
Oct 24 #Python
使用python制作游戏下载进度条的代码(程序说明见注释)
Oct 24 #Python
You might like
Smarty模板类内部原理实例分析
2019/07/03 PHP
jquery easyui的tabs使用时的问题
2010/03/23 Javascript
基于jQuery的表格操作插件
2010/04/22 Javascript
使用jQuery向asp.net Mvc传递复杂json数据-ModelBinder篇
2010/05/07 Javascript
JS操作JSON要领详细总结
2013/08/25 Javascript
JS实现局部选择打印和局部不选择打印
2014/04/03 Javascript
js取得html iframe中的元素和变量值
2014/06/30 Javascript
jQuery新的事件绑定机制on()示例应用
2014/07/18 Javascript
JavaScript对象数组排序函数及六个用法
2015/12/23 Javascript
创建一般js对象的几种方式
2017/01/19 Javascript
JS实现本地存储信息的方法(基于localStorage与userData)
2017/02/18 Javascript
ES6新特性一: let和const命令详解
2017/04/20 Javascript
Javascript中JSON数据分组优化实践及JS操作JSON总结
2017/12/22 Javascript
微信小程序之多文件下载的简单封装示例
2018/01/29 Javascript
NodeJS简单实现WebSocket功能示例
2018/02/10 NodeJs
详解vantUI框架在vue项目中的应用踩坑
2018/12/06 Javascript
微信小程序开发实现的IP地址查询功能示例
2019/03/28 Javascript
Vue中keep-alive 实现后退不刷新并保持滚动位置
2020/03/17 Javascript
Windows系统下使用flup搭建Nginx和Python环境的方法
2015/12/25 Python
学习Python selenium自动化网页抓取器
2018/01/20 Python
浅谈tensorflow1.0 池化层(pooling)和全连接层(dense)
2018/04/27 Python
解决pycharm运行出错,代码正确结果不显示的问题
2018/11/30 Python
对Python的交互模式和直接运行.py文件的区别详解
2019/06/29 Python
python中自带的三个装饰器的实现
2019/11/08 Python
selenium中get_cookies()和add_cookie()的用法详解
2020/01/06 Python
解决pycharm中的run和debug失效无法点击运行
2020/06/09 Python
python元组拆包实现方法
2021/02/28 Python
CSS3中的弹性布局em运用入门详解 1em等于多少像素
2021/02/08 HTML / CSS
Spartoo葡萄牙鞋类网站:线上销售鞋履与时尚配饰
2017/01/11 全球购物
香港时尚女装购物网站:ZAFUL
2017/07/19 全球购物
德国柯吉澳趣味家居:Koziol
2017/08/24 全球购物
荷兰网上买鞋:MooieSchoenen.nl
2017/09/12 全球购物
支部鉴定材料
2014/06/02 职场文书
2014年医院科室工作总结
2014/12/20 职场文书
导游词之徐州-云龙山
2019/09/29 职场文书
教你使用Python pypinyin库实现汉字转拼音
2021/05/27 Python