进一步探究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 12 Python
基于python编写的微博应用
Oct 17 Python
Python中的pprint折腾记
Jan 21 Python
Python遍历zip文件输出名称时出现乱码问题的解决方法
Apr 08 Python
python操作mongodb根据_id查询数据的实现方法
May 20 Python
详解使用pymysql在python中对mysql的增删改查操作(综合)
Jan 18 Python
Python实现简单http服务器
Apr 12 Python
django 使用 request 获取浏览器发送的参数示例代码
Jun 11 Python
python引入不同文件夹下的自定义模块方法
Oct 27 Python
用Python将Excel数据导入到SQL Server的例子
Aug 24 Python
Anaconda和ipython环境适配的实现
Apr 22 Python
有趣的二维码:使用MyQR和qrcode来制作二维码
May 10 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
DC《神奇女侠2》因疫情推迟上映 温子仁新恐怖片《恶性》撤档
2020/04/09 欧美动漫
php添加文章时生成静态HTML文章的实现代码
2013/02/17 PHP
用PHP实现弹出消息提示框的两种方法
2013/12/17 PHP
php中单个数据库字段多列显示(单字段分页、横向输出)
2014/07/28 PHP
让您的菜单不离网站
2006/10/03 Javascript
AJAX的跨域与JSONP(为文章自动添加短址的功能)
2010/01/17 Javascript
浏览器打开层自动缓慢展开收缩实例代码
2013/07/04 Javascript
JavaScript学习笔记之JS对象
2015/01/22 Javascript
Node.js的Express框架使用上手指南
2016/03/12 Javascript
javascript HTML5 canvas实现打砖块游戏
2020/06/18 Javascript
javascript 中null和undefined区分和比较
2017/04/19 Javascript
使用命令行工具npm新创建一个vue项目的方法
2017/12/27 Javascript
微信小程序使用map组件实现检索(定位位置)周边的POI功能示例
2019/01/23 Javascript
Vue组件间通信方法总结(父子组件、兄弟组件及祖先后代组件间)
2019/04/17 Javascript
vue 实现input表单元素的disabled示例
2019/10/28 Javascript
vue 实现click同时传入事件对象和自定义参数
2021/01/29 Vue.js
[01:04:31]DOTA2-DPC中国联赛定级赛 iG vs Magma BO3第二场 1月8日
2021/03/11 DOTA
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
多版本Python共存的配置方法
2017/05/22 Python
Python装饰器限制函数运行时间超时则退出执行
2019/04/09 Python
python3实现在二叉树中找出和为某一值的所有路径(推荐)
2019/12/26 Python
Keras 数据增强ImageDataGenerator多输入多输出实例
2020/07/03 Python
树莓派升级python的具体步骤
2020/07/05 Python
Python基础教程之输入输出和运算符
2020/07/26 Python
Python连接Impala实现步骤解析
2020/08/04 Python
python 5个顶级异步框架推荐
2020/09/09 Python
HTML5 CSS3给网站设计带来出色效果
2009/07/16 HTML / CSS
Free People中国官网:波西米亚风格女装服饰
2016/08/30 全球购物
一篇.NET面试题
2014/09/29 面试题
幼儿园家长评语
2014/02/10 职场文书
研发工程师岗位职责
2014/04/28 职场文书
品质保证书格式
2015/02/28 职场文书
大学生自我评价范文
2015/03/03 职场文书
青年教师听课心得体会
2016/01/15 职场文书
写给汽车4S店的创业计划书,拿来即用!
2019/08/09 职场文书
七年级作文之环保作文
2019/10/17 职场文书