进一步探究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的类实例属性访问规则探讨
Jan 30 Python
python实现的用于搜索文件并进行内容替换的类实例
Jun 28 Python
Python中使用urllib2模块编写爬虫的简单上手示例
Jan 20 Python
Python获取指定字符前面的所有字符方法
May 02 Python
详解Python爬取并下载《电影天堂》3千多部电影
Apr 26 Python
Python之Numpy的超实用基础详细教程
Oct 23 Python
Python Scrapy图片爬取原理及代码实例
Jun 12 Python
Expected conditions模块使用方法汇总代码解析
Aug 13 Python
python如何调用百度识图api
Sep 29 Python
python 简单的调用有道翻译
Nov 25 Python
Python Pygame实战在打砖块游戏的实现
Mar 17 Python
Python爬虫 简单介绍一下Xpath及使用
Apr 26 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
NOT NULL 和NULL
2007/01/15 PHP
PHP htmlspecialchars() 函数实例代码及用法大全
2018/09/18 PHP
javascript prototype原型操作笔记
2009/12/07 Javascript
javascript写的日历类(基于pj)
2010/12/28 Javascript
用js实现判断当前网址的来路如果不是指定的来路就跳转到指定页面
2011/05/02 Javascript
18个非常棒的jQuery代码片段
2015/11/02 Javascript
jquery mobile界面数据刷新的实现方法
2016/05/28 Javascript
巧用jQuery选择器提高写表单效率的方法
2016/08/19 Javascript
完美实现js焦点轮播效果(二)(图片可滚动)
2017/03/07 Javascript
JavaScript使用Ajax上传文件的示例代码
2017/08/10 Javascript
vue+Vue Router多级侧导航切换路由(页面)的实现代码
2018/12/20 Javascript
详解vue 不同环境配置不同的打包命令
2019/04/07 Javascript
JS设置自定义快捷键并实现图片上下左右移动
2019/10/17 Javascript
vue和小程序项目中使用iconfont的方法
2020/05/19 Javascript
JavaScript缺少insertAfter解决方案
2020/07/03 Javascript
js实现简单扫雷
2020/11/27 Javascript
[03:44]2015国际邀请赛选手档案—Cloud9.NoTail
2015/07/28 DOTA
Python字符串逐字符或逐词反转方法
2015/05/21 Python
从源码解析Python的Flask框架中request对象的用法
2016/06/02 Python
python如何在终端里面显示一张图片
2016/08/17 Python
Django 导出 Excel 代码的实例详解
2017/08/11 Python
Python数据结构之双向链表的定义与使用方法示例
2018/01/16 Python
详解小白之KMP算法及python实现
2019/04/04 Python
python文字和unicode/ascll相互转换函数及简单加密解密实现代码
2019/08/12 Python
用Python做一个久坐提醒小助手的示例代码
2020/02/10 Python
Python实现多线程下载脚本的示例代码
2020/04/03 Python
Python实现手绘图效果实例分享
2020/07/22 Python
Python尾递归优化实现代码及原理详解
2020/10/09 Python
法国在线药房:Shop Pharmacie
2019/11/26 全球购物
预备党员党校学习自我评价分享
2013/11/12 职场文书
项目经理的岗位职责
2013/11/23 职场文书
群众路线党课主持词
2014/04/01 职场文书
计划生育证明格式范本
2014/09/12 职场文书
行为规范主题班会
2015/08/13 职场文书
springBoot基于webSocket实现扫码登录
2021/06/22 Java/Android
Sleuth+logback 设置traceid 及自定义信息方式
2021/07/26 Java/Android