进一步探究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搭建Django应用程序步骤及版本冲突问题解决
Nov 19 Python
Python实现的网页截图功能【PyQt4与selenium组件】
Jul 12 Python
Python多进程与服务器并发原理及用法实例分析
Aug 21 Python
python实现Zabbix-API监控
Sep 17 Python
wxPython实现分隔窗口
Nov 19 Python
Python爬虫爬取百度搜索内容代码实例
Jun 05 Python
python主要用于哪些方向
Jul 05 Python
Python字典fromkeys()方法使用代码实例
Jul 20 Python
Python实例方法、类方法、静态方法区别详解
Sep 05 Python
Pycharm添加虚拟解释器报错问题解决方案
Oct 13 Python
matplotlib绘制多子图共享鼠标光标的方法示例
Jan 08 Python
浅析python字符串前加r、f、u、l 的区别
Jan 24 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执行linux系统命令的常用函数使用说明
2010/04/27 PHP
在php中判断一个请求是ajax请求还是普通请求的方法
2011/06/28 PHP
PHP源码之explode使用说明
2011/08/05 PHP
php函数array_merge用法一例(合并同类数组)
2013/02/03 PHP
php出现内存位置访问无效错误问题解决方法
2014/08/16 PHP
php while循环控制的简单实例
2016/05/30 PHP
PhpStorm配置Xdebug调试的方法步骤
2019/02/02 PHP
PHP 并发场景的几种解决方案
2019/06/14 PHP
一个JavaScript继承的实现
2006/10/24 Javascript
javascript XMLHttpRequest对象全面剖析
2010/04/24 Javascript
浅析js设置控件的readonly与enabled属性问题
2013/12/25 Javascript
javascript函数定义的几种区别小结
2014/01/06 Javascript
浅析vue数据绑定
2017/01/17 Javascript
layui.js实现的表单验证功能示例
2017/11/15 Javascript
flexible.js实现移动端rem适配方案
2020/04/07 Javascript
vue商城中商品“筛选器”功能的实现代码
2020/07/01 Javascript
[03:02]辉夜杯主赛事第二日 每日之星
2015/12/27 DOTA
python进阶教程之词典、字典、dict
2014/08/29 Python
Python实现的检测web服务器健康状况的小程序
2014/09/17 Python
Python使用urllib模块的urlopen超时问题解决方法
2014/11/08 Python
Python写的Tkinter程序屏幕居中方法
2015/03/10 Python
Python中尝试多线程编程的一个简明例子
2015/04/07 Python
python协程用法实例分析
2015/06/04 Python
python实现简单的文字识别
2018/11/27 Python
Python编程图形库之Pillow使用方法讲解
2018/12/28 Python
python中的 zip函数详解及用法举例
2020/02/16 Python
Pycharm和Idea支持的vim插件的方法
2020/02/21 Python
Python 从attribute到property详解
2020/03/05 Python
Python实现捕获异常发生的文件和具体行数
2020/04/25 Python
Python中Selenium库使用教程详解
2020/07/23 Python
德国在线订购鲜花:Fleurop
2018/08/25 全球购物
欧克利英国官网:Oakley英国
2019/08/24 全球购物
年底个人总结范文
2015/03/10 职场文书
2015中学学校工作总结
2015/07/20 职场文书
thinkphp 获取控制器及控制器方法
2021/04/16 PHP
Vue和Flask通信的实现
2021/05/19 Vue.js