进一步探究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 相关文章推荐
win7安装python生成随机数代码分享
Dec 27 Python
Python数据结构与算法之链表定义与用法实例详解【单链表、循环链表】
Sep 28 Python
pandas object格式转float64格式的方法
Apr 10 Python
利用python GDAL库读写geotiff格式的遥感影像方法
Nov 29 Python
Python3中lambda表达式与函数式编程讲解
Jan 14 Python
Python删除n行后的其他行方法
Jan 28 Python
pyinstaller打包单个exe后无法执行错误的解决方法
Jun 21 Python
2020最新pycharm汉化安装(python工程狮亲测有效)
Apr 26 Python
Python读取JSON数据操作实例解析
May 18 Python
pycharm导入源码的具体步骤
Aug 04 Python
从零开始的TensorFlow+VScode开发环境搭建的步骤(图文)
Aug 31 Python
Jupyter安装链接aconda实现过程图解
Nov 02 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
收音机史话 - 1960年代前后的DIY
2021/03/02 无线电
source.php查看源文件
2006/12/09 PHP
微信公众平台开发实现2048游戏的方法
2015/04/15 PHP
php 参数过滤、数据过滤详解
2015/10/26 PHP
不安全的常用的js写法
2009/09/15 Javascript
使用jQuery清空file文件域的解决方案
2013/04/12 Javascript
JS代码判断IE6,IE7,IE8,IE9的函数代码
2013/08/02 Javascript
使用 js+正则表达式为关键词添加链接
2014/11/11 Javascript
js实现格式化金额,字符,时间的方法
2015/02/26 Javascript
jQuery 遍历函数详解
2015/07/05 Javascript
用JavaScript实现PHP的urlencode与urldecode函数
2015/08/13 Javascript
vue.js中过滤器的使用教程
2017/06/08 Javascript
Vue学习笔记进阶篇之单元素过度
2017/07/19 Javascript
67 个节约开发时间的前端开发者的工具、库和资源
2017/09/12 Javascript
NodeJs 实现简单WebSocket即时通讯的示例代码
2019/08/05 NodeJs
vue+node 实现视频在线播放的实例代码
2020/10/19 Javascript
Python作用域用法实例详解
2016/03/15 Python
Python 读写文件和file对象的方法(推荐)
2016/09/12 Python
Python字符串处理实例详解
2017/05/18 Python
Python 实现取矩阵的部分列,保存为一个新的矩阵方法
2018/11/14 Python
Appium+python自动化之连接模拟器并启动淘宝APP(超详解)
2019/06/17 Python
python实现用户名密码校验
2020/03/18 Python
CSS3实现滚动条动画效果代码分享
2016/08/03 HTML / CSS
css3通过scale()、rotate()实现放大、旋转
2020/03/19 HTML / CSS
说说在weblogic中开发消息Bean时的persistent与non-persisten的差别
2013/04/07 面试题
事务机电主管工作职责
2014/02/25 职场文书
法学专业毕业生自荐信
2014/06/11 职场文书
校园标语大全
2014/06/19 职场文书
2014年化验员工作总结
2014/11/18 职场文书
2015年客服工作总结范文
2015/04/02 职场文书
城南旧事观后感
2015/06/11 职场文书
暑假开始了,你的暑假学习计划写好了吗?
2019/07/04 职场文书
导游词之苏州寒山寺
2019/12/05 职场文书
Python中常见的反爬机制及其破解方法总结
2021/06/10 Python
阿里云国际版 使用Nginx作为HTTPS转发代理服务器
2022/05/11 Servers
使用CSS实现音波加载效果
2023/05/07 HTML / CSS