进一步探究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封装对象实现时间效果
Apr 23 Python
Python的高级Git库 Gittle
Sep 22 Python
利用python代码写的12306订票代码
Dec 20 Python
详解Python 2.6 升级至 Python 2.7 的实践心得
Apr 27 Python
使用Numpy读取CSV文件,并进行行列删除的操作方法
Jul 04 Python
python中的常量和变量代码详解
Jul 25 Python
详解python读取和输出到txt
Mar 29 Python
python 3.6.7实现端口扫描器
Sep 04 Python
pyinstaller 3.6版本通过pip安装失败的解决办法(推荐)
Jan 18 Python
python 比较字典value的最大值的几种方法
Apr 17 Python
django admin管理工具自定义时间区间筛选器DateRangeFilter介绍
May 19 Python
pytorch中Schedule与warmup_steps的用法说明
May 24 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中jpgraph类库的使用介绍
2013/08/08 PHP
php无限遍历文件夹示例分享
2014/03/04 PHP
php中strtotime函数性能分析
2016/11/20 PHP
PHP生成随机密码4种方法及性能对比
2020/12/11 PHP
JavaScript中的事件处理
2008/01/16 Javascript
js各种验证文本框输入格式(正则表达式)
2010/10/22 Javascript
json格式的时间显示为正常年月日的方法
2013/09/08 Javascript
JavaScript使用indexOf获得子字符串在字符串中位置的方法
2015/04/06 Javascript
jQuery左侧大图右侧小图焦点图幻灯切换代码分享
2015/08/19 Javascript
JavaScript判断FileUpload控件上传文件类型
2015/09/28 Javascript
js实现的星星评分功能函数
2015/12/09 Javascript
JavaScript Date对象详解
2016/03/01 Javascript
使用struts2+Ajax+jquery验证用户名是否已被注册
2016/03/22 Javascript
微信小程序中实现一对多发消息详解及实例代码
2017/02/14 Javascript
jQuery实现的简单在线计算器功能
2017/05/11 jQuery
用纯Node.JS弹出Windows系统消息提示框实例(MessageBox)
2017/05/17 Javascript
js实现图片粘贴上传到服务器并展示的实例
2017/11/08 Javascript
JavaScript设计模式之构造函数模式实例教程
2018/07/02 Javascript
详解JavaScript中的函数、对象
2019/04/01 Javascript
vue2.0 实现富文本编辑器功能
2019/05/26 Javascript
微信小程序 flexbox layout快速实现基本布局的解决方案
2020/03/24 Javascript
[55:56]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.22
2019/09/05 DOTA
在Python中操作文件之seek()方法的使用教程
2015/05/24 Python
Python日期的加减等操作的示例
2017/08/15 Python
Python发送http请求解析返回json的实例
2018/03/26 Python
python flask web服务实现更换默认端口和IP的方法
2019/07/26 Python
Python并发concurrent.futures和asyncio实例
2020/05/04 Python
Python3 webservice接口测试代码详解
2020/06/23 Python
pytorch 计算ConvTranspose1d输出特征大小方式
2020/06/23 Python
用css3制作纸张效果(外翻卷角)
2013/02/01 HTML / CSS
生日庆典策划方案
2014/06/02 职场文书
乡镇干部个人对照检查材料(群众路线)
2014/09/26 职场文书
故意伤害辩护词
2015/05/21 职场文书
小学生大队委竞选稿
2015/11/20 职场文书
工作一年自我鉴定
2019/06/20 职场文书
Spring Data JPA框架的核心概念和Repository接口
2022/04/28 Java/Android