进一步探究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登录Dr.com思路以及代码分享
Jun 25 Python
python里对list中的整数求平均并排序
Sep 12 Python
Python基本语法经典教程
Mar 11 Python
Python爬虫番外篇之Cookie和Session详解
Dec 27 Python
python实现嵌套列表平铺的两种方法
Nov 08 Python
Python快速转换numpy数组中Nan和Inf的方法实例说明
Feb 21 Python
在Python中append以及extend返回None的例子
Jul 20 Python
Django 实现前端图片压缩功能的方法
Aug 07 Python
Django websocket原理及功能实现代码
Nov 14 Python
python爬虫用scrapy获取影片的实例分析
Nov 23 Python
Django一小时写出账号密码管理系统
Apr 29 Python
OpenCV绘制圆端矩形的示例代码
Aug 30 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
星际玩家的三大定律
2020/03/04 星际争霸
使用PHP 5.0创建图形的巧妙方法
2010/10/12 PHP
php调用mysql数据 dbclass类
2011/05/07 PHP
PHP图片上传代码
2013/11/04 PHP
PHP文件锁定写入实例解析
2014/07/14 PHP
详解PHP+AJAX无刷新分页实现方法
2015/11/03 PHP
基于jquery的自定义鼠标提示效果 jquery.toolTip
2010/11/14 Javascript
JQuery将文本转化成JSON对象需要注意的问题
2011/05/09 Javascript
jquery和ajax的关系详细介绍
2013/11/29 Javascript
jQuery实现页面内锚点平滑跳转特效的方法总结
2015/05/11 Javascript
Jquery实现瀑布流布局(备有详细注释)
2015/07/31 Javascript
深入理解JavaScript中的call、apply、bind方法的区别
2016/05/30 Javascript
jQuery中ScrollTo用法示例
2016/09/04 Javascript
微信小程序 swiper组件轮播图详解及实例
2016/11/16 Javascript
获取JavaScript异步函数的返回值
2016/12/21 Javascript
详解Angular之constructor和ngOnInit差异及适用场景
2017/06/22 Javascript
Python + selenium自动化环境搭建的完整步骤
2018/05/19 Python
python通过微信发送邮件实现电脑关机
2018/06/20 Python
Django Admin实现三级联动的示例代码(省市区)
2018/06/22 Python
python+pyqt5编写md5生成器
2019/03/18 Python
详解css3使用transform出现字体模糊的解决办法
2020/10/16 HTML / CSS
html5 canvas绘制网络字体的常用方法
2019/08/26 HTML / CSS
万得城电器土耳其网站:欧洲第一大电子产品零售商
2016/10/07 全球购物
简单介绍Object类的功能、常用方法
2013/10/02 面试题
Final类有什么特点
2012/04/25 面试题
大学生个人自我鉴定
2013/12/03 职场文书
人事部专员岗位职责
2014/03/04 职场文书
常务副总经理岗位职责
2014/04/12 职场文书
工作分析计划书
2014/04/30 职场文书
学校安全防火方案
2014/06/07 职场文书
幼儿园班级管理心得体会
2016/01/07 职场文书
详解Nginx启动失败的几种错误处理
2021/04/01 Servers
使用springboot暴露oracle数据接口的问题
2021/05/07 Oracle
我对PyTorch dataloader里的shuffle=True的理解
2021/05/20 Python
Python测试框架pytest高阶用法全面详解
2022/06/01 Python
win10拖拽文件时崩溃怎么解决?win10文件不能拖拽问题解决方法
2022/08/14 数码科技