进一步探究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转换HTML到Text纯文本的方法
Jan 15 Python
python实现2048小游戏
Mar 30 Python
python代码 if not x: 和 if x is not None: 和 if not x is None:使用介绍
Sep 21 Python
AI人工智能 Python实现人机对话
Nov 13 Python
在pycharm上mongodb配置及可视化设置方法
Nov 30 Python
Python中xml和json格式相互转换操作示例
Dec 05 Python
如何在Django中设置定时任务的方法示例
Jan 18 Python
Python使用到第三方库PyMuPDF图片与pdf相互转换
May 03 Python
python实现邮件发送功能
Aug 10 Python
编写python代码实现简单抽奖器
Oct 20 Python
利用Python发送邮件或发带附件的邮件
Nov 12 Python
PyCharm最新激活码PyCharm2020.2.3有效
Nov 18 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 str_pad 函数使用详解
2009/01/13 PHP
解析php中static,const与define的使用区别
2013/06/18 PHP
在windows平台上构建自己的PHP实现方法(仅适用于php5.2)
2013/07/05 PHP
php统计时间和内存使用情况示例分享
2014/03/13 PHP
基于jQuery架构javascript基础体系
2011/01/01 Javascript
把文本中的URL地址转换为可点击链接的JavaScript、PHP自定义函数
2014/07/29 Javascript
jquery实现一个简单好用的弹出框
2014/09/26 Javascript
基于Node.js + WebSocket打造即时聊天程序嗨聊
2016/11/29 Javascript
老生常谈js中的MVC
2017/07/25 Javascript
Angular实现响应式表单
2017/08/04 Javascript
js排序与重组的实例讲解
2017/08/28 Javascript
switchery按钮的使用方法
2017/12/18 Javascript
讲解vue-router之什么是编程式路由
2018/05/28 Javascript
Nodejs中获取当前函数被调用的行数及文件名详解
2018/12/12 NodeJs
Angular2 自定义表单验证器的实现方法
2018/12/14 Javascript
js的新生代垃圾回收知识点总结
2019/08/22 Javascript
JS实现可视化音频效果的实例代码
2020/01/16 Javascript
微信小程序实现点击生成随机验证码
2020/09/09 Javascript
详解Typescript里的This的使用方法
2021/01/08 Javascript
编写Python小程序来统计测试脚本的关键字
2016/03/12 Python
Python中shutil模块的常用文件操作函数用法示例
2016/07/05 Python
python使用PyCharm进行远程开发和调试
2017/11/02 Python
对pandas的算术运算和数据对齐实例详解
2018/12/22 Python
django解决跨域请求的问题详解
2019/01/20 Python
python实现在多维数组中挑选符合条件的全部元素
2019/11/26 Python
实例讲解Python 迭代器与生成器
2020/07/08 Python
python实现不同数据库间数据同步功能
2021/02/25 Python
纯CSS3实现图片无间断轮播效果
2016/08/25 HTML / CSS
玩具反斗城西班牙网上商城:ToysRUs西班牙
2017/01/19 全球购物
6号汽车旅馆预订:Motel 6
2018/02/11 全球购物
100%有机精油,美容油:House of Pure Essence
2018/10/30 全球购物
Jacadi Paris英国官网:法国童装品牌
2019/08/09 全球购物
教你怎样写好自我评价
2013/10/05 职场文书
幼儿评语大全
2014/04/30 职场文书
管理人员岗位职责
2015/02/14 职场文书
胡桃夹子观后感
2015/06/11 职场文书