进一步探究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多进程multiprocessing用法实例分析
Aug 18 Python
对python requests的content和text方法的区别详解
Oct 11 Python
对python借助百度云API对评论进行观点抽取的方法详解
Feb 21 Python
在win10和linux上分别安装Python虚拟环境的方法步骤
May 09 Python
Django框架基础模板标签与filter使用方法详解
Jul 23 Python
用Python绘制漫步图实例讲解
Feb 26 Python
Python通过正则库爬取淘宝商品信息代码实例
Mar 02 Python
使用tensorflow根据输入更改tensor shape
Jun 23 Python
scrapy-redis分布式爬虫的搭建过程(理论篇)
Sep 29 Python
python中_del_还原数据的方法
Dec 09 Python
python regex库实例用法总结
Jan 03 Python
使用Python制作一个数据预处理小工具(多种操作一键完成)
Feb 07 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中$this和$that指针使用实例
2015/01/06 PHP
php解析字符串里所有URL地址的方法
2015/04/03 PHP
php去除二维数组的重复项方法
2015/11/03 PHP
深入理解PHP内核(二)之SAPI探究
2015/11/10 PHP
鼠标移动到一张图片时变为另一张图片
2006/12/05 Javascript
js trim函数 去空格函数与正则集锦
2009/11/20 Javascript
js+数组实现网页上显示时间/星期几的实用方法
2013/01/18 Javascript
javascript模块化是什么及其优缺点介绍
2013/09/02 Javascript
自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框
2015/12/12 Javascript
JavaScript Promise 用法
2016/06/14 Javascript
jQuery如何获取动态添加的元素
2016/06/24 Javascript
jQuery flip插件实现的翻牌效果示例【附demo源码下载】
2016/09/20 Javascript
JQ中$(window).load和$(document).ready区别与执行顺序
2017/03/01 Javascript
jQuery简单实现对数组去重及排序操作实例
2017/10/31 jQuery
jQuery创建及操作xml格式数据示例
2018/05/26 jQuery
js删除数组中某几项的方法总结
2019/01/16 Javascript
vue实现购物车抛物线小球动画效果的方法详解
2019/02/13 Javascript
vue 使用async写数字动态加载效果案例
2020/07/18 Javascript
python通过pil将图片转换成黑白效果的方法
2015/03/16 Python
在Python中操作字符串之rstrip()方法的使用
2015/05/19 Python
Python实现删除文件但保留指定文件
2015/06/21 Python
python实现图片处理和特征提取详解
2017/11/13 Python
Python中循环引用(import)失败的解决方法
2018/04/22 Python
python openpyxl使用方法详解
2019/07/18 Python
使用Python实现图像标记点的坐标输出功能
2019/08/14 Python
python2和python3应该学哪个(python3.6与python3.7的选择)
2019/10/01 Python
教师申诉制度
2014/01/29 职场文书
运动会入场式解说词
2014/02/18 职场文书
高中毕业生的个人自我评价
2014/02/21 职场文书
技术比武方案
2014/05/19 职场文书
煤矿开采专业求职信
2014/07/08 职场文书
企业员工爱岗敬业演讲稿
2014/08/26 职场文书
平面设计师岗位职责
2014/09/18 职场文书
追悼词范文大全
2015/06/23 职场文书
导游词之杭州西湖
2019/09/19 职场文书
MYSQL 无法识别中文的永久解决方法
2021/06/03 MySQL