进一步探究Python的装饰器的运用


Posted in Python onMay 05, 2015

装饰器在 python 中用的相当广泛,如果你用过 python 的一些 web 框架,那么一定对其中的 “ route() 装饰器” 不陌生,今天咱们再看一个具体的案例。

咱们来模拟一个场景,需要你去抓去一个页面,然后这个页面有好多url也要分别去抓取,而进入这些子url后,还有数据要抓取。简单点,我们就按照三层来看,那我们的代码就是如下:
 

def func_top(url):
  data_dict= {}
 
  #在页面上获取到子url
  sub_urls = xxxx
 
  data_list = []
  for it in sub_urls:
    data_list.append(func_sub(it))
 
  data_dict['data'] = data_list
 
  return data_dict
 
def func_sub(url):
  data_dict= {}
 
  #在页面上获取到子url
  bottom_urls = xxxx
 
  data_list = []
  for it in bottom_urls:
    data_list.append(func_bottom(it))
 
  data_dict['data'] = data_list
 
  return data_dict
 
def func_bottom(url):
  #获取数据
  data = xxxx
  return data

func_top是上层页面的处理函数,func_sub是子页面的处理函数,func_bottom是最深层页面的处理函数,func_top会在取到子页面url后遍历调用func_sub,func_sub也是同样。
如果正常情况下,这样确实已经满足需求了,但是偏偏这个你要抓取的网站可能极不稳定,经常链接不上,导致数据拿不到。
于是这个时候你有两个选择:
1.遇到错误就停止,之后重新从断掉的位置开始重新跑
2.遇到错误继续,但是要在之后重新跑一遍,这个时候已经有的数据不希望再去网站拉一次,而只去拉没有取到的数据
对第一种方案基本无法实现,因为如果别人网站的url调整顺序,那么你记录的位置就无效了。那么只有第二种方案,说白了,就是要把已经拿到的数据cache下来,等需要的时候,直接从cache里面取。
OK,目标已经有了,怎么实现呢?
如果是在C++中的,这是个很麻烦的事情,而且写出来的代码必定丑陋无比,然而庆幸的是,我们用的是python,而python对函数有装饰器。
所以实现方案也就有了:
定义一个装饰器,如果之前取到数据,就直接取cache的数据;如果之前没有取到,那么就从网站拉取,并且存入cache中.
代码如下:
 

import os
import hashlib
 
def deco_args_recent_cache(category='dumps'):
  '''
  装饰器,返回最新cache的数据
  '''
  def deco_recent_cache(func):
    def func_wrapper(*args, **kargs):
      sig = _mk_cache_sig(*args, **kargs)
      data = _get_recent_cache(category, func.__name__, sig)
      if data is not None:
        return data
 
      data = func(*args, **kargs)
      if data is not None:
        _set_recent_cache(category, func.__name__, sig, data)
      return data
 
    return func_wrapper
 
  return deco_recent_cache
 
def _mk_cache_sig(*args, **kargs):
  '''
  通过传入参数,生成唯一标识
  '''
  src_data = repr(args) + repr(kargs)
  m = hashlib.md5(src_data)
  sig = m.hexdigest()
  return sig
 
def _get_recent_cache(category, func_name, sig):
  full_file_path = '%s/%s/%s' % (category, func_name, sig)
  if os.path.isfile(full_file_path):
    return eval(file(full_file_path,'r').read())
  else:
    return None
 
def _set_recent_cache(category, func_name, sig, data):
  full_dir_path = '%s/%s' % (category, func_name)
  if not os.path.isdir(full_dir_path):
    os.makedirs(full_dir_path)
 
  full_file_path = '%s/%s/%s' % (category, func_name, sig)
  f = file(full_file_path, 'w+')
  f.write(repr(data))
  f.close()

然后,我们只需要在每个func_top,func_sub,func_bottom都加上deco_args_recent_cache这个装饰器即可~~
搞定!这样做最大的好处在于,因为top,sub,bottom,每一层都会dump数据,所以比如某个sub层数据dump之后,是根本不会走到他所对应的bottom层的,减少了大量的开销!
OK,就这样~ 人生苦短,我用python!

注:

python3 已经原生支持了这种功能!链接如下:

http://docs.python.org/py3k/whatsnew/3.2.html#functools

推荐阅读:

https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize

Python 相关文章推荐
python实现去除下载电影和电视剧文件名中的多余字符的方法
Sep 23 Python
Python的装饰器模式与面向切面编程详解
Jun 21 Python
开始着手第一个Django项目
Jul 15 Python
python 寻找优化使成本函数最小的最优解的方法
Dec 28 Python
python实现输入数字的连续加减方法
Jun 22 Python
python把数组中的数字每行打印3个并保存在文档中的方法
Jul 17 Python
Python理解递归的方法总结
Jan 28 Python
Django之无名分组和有名分组的实现
Apr 16 Python
详解Python3除法之真除法、截断除法和下取整对比
May 23 Python
Python集中化管理平台Ansible介绍与YAML简介
Jun 12 Python
Python3自定义http/https请求拦截mitmproxy脚本实例
May 11 Python
python em算法的实现
Oct 03 Python
Python获取任意xml节点值的方法
May 05 #Python
Python实现方便使用的级联进度信息实例
May 05 #Python
Python封装shell命令实例分析
May 05 #Python
用Python中的字典来处理索引统计的方法
May 05 #Python
python递归计算N!的方法
May 05 #Python
浅谈Python中数据解析
May 05 #Python
探究Python多进程编程下线程之间变量的共享问题
May 05 #Python
You might like
PHP面向对象的使用教程 简单数据库连接
2006/11/25 PHP
常用的php对象类型判断
2008/08/27 PHP
为PHP5.4开启Zend OPCode缓存
2014/12/26 PHP
调用WordPress函数统计文章访问量及PHP原生计数器的实现
2016/03/21 PHP
简单理解PHP的面向对象编程方式
2016/05/17 PHP
Yii2框架中日志的使用方法分析
2017/05/22 PHP
ASP小贴士/ASP Tips javascript tips可以当桌面
2009/12/10 Javascript
js里的prototype使用示例
2010/11/19 Javascript
输入框的字数时时统计—关于 onpropertychange 和 oninput 使用
2011/10/21 Javascript
JavaScript 模拟类机制及私有变量的方法及思路
2013/07/10 Javascript
js实现数组去重、判断数组以及对象中的内容是否相同
2013/11/29 Javascript
javascript实现dom动态创建省市纵向列表菜单的方法
2015/05/14 Javascript
javascript跨域总结之window.name实现的跨域数据传输
2015/11/01 Javascript
vue Render中slots的使用的实例代码
2017/07/19 Javascript
React Native悬浮按钮组件的示例代码
2018/04/05 Javascript
使用NestJS开发Node.js应用的方法
2018/12/03 Javascript
用vuex写了一个购物车H5页面的示例代码
2018/12/04 Javascript
利用Webpack实现小程序多项目管理的方法
2019/02/25 Javascript
JavaScript解析JSON数据示例
2019/07/16 Javascript
js 数据类型判断的方法
2020/12/03 Javascript
[55:35]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第二场 1月22日
2021/03/11 DOTA
pandas创建新Dataframe并添加多行的实例
2018/04/08 Python
python利用re,bs4,requests模块获取股票数据
2019/07/29 Python
Python二维数组实现求出3*3矩阵对角线元素的和示例
2019/11/29 Python
使用python实现名片管理系统
2020/06/18 Python
Android Q之气泡弹窗的实现示例
2020/06/23 Python
Html5 实现微信分享及自定义内容的流程
2019/08/20 HTML / CSS
AmazeUI 评论列表的实现示例
2020/08/13 HTML / CSS
美国户外服装和装备购物网站:Outland USA
2020/03/22 全球购物
int和Integer有什么区别
2013/05/25 面试题
怎样声明一个匿名的内部类
2016/06/01 面试题
日语专业个人求职信范文
2014/02/02 职场文书
党员自我评议对照检查材料
2014/09/27 职场文书
运动会广播稿50字-100字
2014/10/11 职场文书
2014年办公室个人工作总结
2014/11/12 职场文书
大学生自荐书范文
2015/03/05 职场文书