进一步探究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读写unicode文件的方法
Jul 10 Python
Unicode和Python的中文处理
Mar 19 Python
Python中with及contextlib的用法详解
Jun 08 Python
pandas数据处理基础之筛选指定行或者指定列的数据
May 03 Python
Python3中lambda表达式与函数式编程讲解
Jan 14 Python
python 求一个列表中所有元素的乘积实例
Jun 11 Python
python SVD压缩图像的实现代码
Nov 05 Python
Django对接支付宝实现支付宝充值金币功能示例
Dec 17 Python
python飞机大战pygame游戏背景设计详解
Dec 17 Python
Python unittest生成测试报告过程解析
Sep 08 Python
Python常用base64 md5 aes des crc32加密解密方法汇总
Nov 06 Python
关于PySnooper 永远不要使用print进行调试的问题
Mar 04 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 分页原理分析,大家可以看看
2009/12/21 PHP
php单文件版在线代码编辑器
2015/03/12 PHP
PHP 数组基本操作小结(推荐)
2016/06/13 PHP
PHP插件PHPMailer发送邮件功能
2017/02/28 PHP
Prototype Hash对象 学习
2009/07/19 Javascript
40款非常棒的jQuery 插件和制作教程(系列二)
2011/11/02 Javascript
jQuery打印指定区域Html页面并自动分页
2014/07/04 Javascript
jQuery中:lt选择器用法实例
2014/12/29 Javascript
JavaScript判断手机号运营商是移动、联通、电信还是其他(代码简单)
2015/09/25 Javascript
JavaScript常用本地对象小结
2016/03/28 Javascript
Web安全测试之XSS实例讲解
2016/08/15 Javascript
bootstrap选项卡使用方法解析
2017/01/11 Javascript
easyui关于validatebox实现多重规则验证的方法(必看)
2017/04/12 Javascript
使用Nodejs连接mongodb数据库的实现代码
2017/08/21 NodeJs
详解在vue-test-utils中mock全局对象
2018/11/07 Javascript
jquery中attr、prop、data区别与用法分析
2019/09/25 jQuery
在vue中实现清除echarts上次保留的数据(亲测有效)
2020/09/09 Javascript
解决vue init webpack 下载依赖卡住不动的问题
2020/11/09 Javascript
[10:39]DOTA2上海特级锦标赛音乐会纪录片
2016/03/21 DOTA
Python 2.x如何设置命令执行的超时时间实例
2017/10/19 Python
mac下给python3安装requests库和scrapy库的实例
2018/06/13 Python
对Python 内建函数和保留字详解
2018/10/15 Python
Python代码生成视频的缩略图的实例讲解
2019/12/22 Python
PyTorch 随机数生成占用 CPU 过高的解决方法
2020/01/13 Python
python中get和post有什么区别
2020/06/19 Python
Python读取yaml文件的详细教程
2020/07/21 Python
推荐WEB开发者最佳HTML5和CSS3代码生成器
2015/11/24 HTML / CSS
Goodee官方商店:迷你投影仪
2021/03/15 全球购物
应届生护士求职信
2013/11/01 职场文书
大学本科毕业生的自我鉴定范文
2013/11/19 职场文书
战友聚会邀请函
2014/01/18 职场文书
网上祭英烈活动总结
2015/02/04 职场文书
2015年电厂工作总结范文
2015/05/13 职场文书
宝葫芦的秘密观后感
2015/06/11 职场文书
创业计划书之o2o水果店
2019/08/30 职场文书
vue项目两种方式实现竖向表格的思路分析
2021/04/28 Vue.js