进一步探究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实现的简单抽奖系统实例
May 22 Python
Python下rrdtool模块的基本使用方法
Nov 13 Python
深入浅析Python2.x和3.x版本的主要区别
Nov 30 Python
Python实现查找数组中任意第k大的数字算法示例
Jan 23 Python
浅谈python中get pass用法
Mar 19 Python
Python使用tkinter模块实现推箱子游戏
Oct 08 Python
在Python 的线程中运行协程的方法
Feb 24 Python
Python3内置函数chr和ord实现进制转换
Jun 05 Python
Python list和str互转的实现示例
Nov 16 Python
python中xlrd模块的使用详解
Feb 01 Python
pytorch交叉熵损失函数的weight参数的使用
May 24 Python
Python数据结构之队列详解
Mar 21 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/04/09 欧美动漫
PHILIPS AE3805收音机的分析打磨
2021/03/02 无线电
最贵的咖啡是怎么产生的,它的风味怎么样?
2021/03/04 新手入门
关于Zend Studio 配色方案插件的介绍
2013/06/24 PHP
PHP把JPEG图片转换成Progressive JPEG的方法
2014/06/30 PHP
Zend Framework教程之Zend_Config_Ini用法分析
2016/03/23 PHP
Laravel框架搜索分页功能示例
2019/02/01 PHP
{}与function(){}选用空对象{}来存放keyValue
2012/05/23 Javascript
JS分页控件 可用于无刷新分页
2013/07/23 Javascript
jquery组件使用中遇到的问题整理及解决
2014/02/21 Javascript
JavaScript两种跨域技术全面介绍
2014/04/16 Javascript
jquery动态加载js/css文件方法(自写小函数)
2014/10/11 Javascript
JavaScript中的console.dir()函数介绍
2014/12/29 Javascript
HTML5实现留言和回复页面样式
2015/07/22 Javascript
javascript下拉列表菜单的实现方法
2015/11/18 Javascript
浅析在javascript中创建对象的各种模式
2016/05/06 Javascript
js控件Kindeditor实现图片自动上传功能
2020/07/20 Javascript
jQuery模拟下拉框选择对应菜单的内容
2017/03/07 Javascript
Vue 父子组件数据传递的四种方式( inheritAttrs + $attrs + $listeners)
2018/05/04 Javascript
如何用webpack4.0撸单页/多页脚手架 (jquery, react, vue, typescript)
2019/06/18 jQuery
详解vuex的简单todolist例子
2019/07/14 Javascript
vue图片裁剪插件vue-cropper使用方法详解
2020/12/16 Vue.js
python迭代器的使用方法实例
2013/11/21 Python
PyQt5 QSerialPort子线程操作的实现
2018/04/21 Python
Python生成MD5值的两种方法实例分析
2019/04/26 Python
用sqlalchemy构建Django连接池的实例
2019/08/29 Python
python数据持久存储 pickle模块的基本使用方法解析
2019/08/30 Python
django框架cookie和session用法实例详解
2019/12/10 Python
Python 音频生成器的实现示例
2019/12/24 Python
Python如何实现爬取B站视频
2020/05/20 Python
StubHub澳大利亚:购买或出售您的门票
2019/08/01 全球购物
自然健康的概念:Natural Healthy Concepts
2020/01/26 全球购物
C语言变量的命名规则都有哪些
2013/12/27 面试题
新年主持词
2014/03/27 职场文书
导游词之襄阳古城
2019/09/27 职场文书
导游词之杭州岳王庙
2019/11/13 职场文书