进一步探究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框架中的中间件
Jul 24 Python
python中(str,list,tuple)基础知识汇总
Feb 20 Python
Django项目实战之用户头像上传与访问的示例
Apr 21 Python
Python 十六进制整数与ASCii编码字符串相互转换方法
Jul 09 Python
python中aioysql(异步操作MySQL)的方法
Apr 11 Python
Python3+PyInstall+Sciter解决报错缺少dll、html等文件问题
Jul 15 Python
利用rest framework搭建Django API过程解析
Aug 31 Python
python打开文件的方式有哪些
Jun 29 Python
python3 循环读取excel文件并写入json操作
Jul 14 Python
Python JSON常用编解码方法代码实例
Sep 05 Python
python按照list中字典的某key去重的示例代码
Oct 13 Python
python 从list中随机取值的方法
Nov 16 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二分法在IP地址查询中的应用
2008/08/12 PHP
php入门学习知识点一 PHP与MYSql连接与查询
2011/07/14 PHP
PHP的mysqli_select_db()函数讲解
2019/01/23 PHP
运用Windows XP附带的Msicuu.exe、Msizap.exe来彻底卸载顽固程序
2007/04/21 Javascript
JS小框架 fly javascript framework
2009/11/26 Javascript
JavaScript 学习笔记(七)字符串的连接
2009/12/31 Javascript
通过正则格式化url查询字符串实现代码
2012/12/28 Javascript
将文本输入框内容加入表中的js代码
2013/08/18 Javascript
innerHTML动态添加html代码和脚本兼容多个浏览器
2014/10/11 Javascript
js使用setTimeout实现定时炸弹的方法
2015/04/10 Javascript
详解JavaScript 中的 replace 方法
2016/01/01 Javascript
js基于setTimeout与setInterval实现多线程
2016/06/17 Javascript
javascript js 操作数组 增删改查的简单实现
2016/06/20 Javascript
深入理解Javascript中的作用域链和闭包
2017/04/25 Javascript
基于vue2.0实现的级联选择器
2017/06/09 Javascript
Angular 2父子组件数据传递之@Input和@Output详解(下)
2017/07/05 Javascript
React中使用collections时key的重要性详解
2017/08/07 Javascript
使用Electron构建React+Webpack桌面应用的方法
2017/12/15 Javascript
vue 调用 RESTful风格接口操作
2020/08/11 Javascript
Openlayers实现距离面积测量
2020/09/28 Javascript
python实现自动发送邮件
2018/06/20 Python
Python django框架输入汉字,数字,字符生成二维码实现详解
2019/09/24 Python
python实现猜数游戏
2020/03/27 Python
美国唇部护理专家:Sara Happ
2019/06/19 全球购物
库房主管岗位职责
2013/12/31 职场文书
商务会议邀请函
2014/01/09 职场文书
动员大会主持词
2014/03/20 职场文书
学校政风行风评议心得体会
2014/10/21 职场文书
2015年妇联工作总结范文
2015/04/22 职场文书
初中政教处工作总结
2015/08/12 职场文书
2016年员工年度考核评语
2015/12/02 职场文书
2016年十一促销广告语
2016/01/28 职场文书
高中语文教材(文学文化常识大全一)
2019/08/13 职场文书
Win11 S Mode版本泄露 正式上线后叫做Windows 11 SE
2021/11/21 数码科技
选购到合适的激光打印机
2022/04/21 数码科技
三星 3nm 芯片将于第二季度开始量产
2022/04/29 数码科技