zoukankan      html  css  js  c++  java
  • [python]在场景中理解装饰器

    原来我也自己通过查资料,来学习python的装饰器,但是效果不好。因为没有接触过需要用到装饰器的场景,所以

    一起的资料都只停留在纸面上,但是今天偶然看到了vimer的这篇文章:http://www.vimer.cn/2011/04/python%E8%A3%85%E9%A5%B0%E5%99%A8%E7%9A%84%E4%B8%80%E4%B8%AA%E5%A6%99%E7%94%A8.html

    我们就根据这篇文章的思路来,在场景中理解python装饰器

    其中的一个场景是:爬取数据的时候,目标网站,不稳定。爬虫在爬取数据的时候可能异常。

    解决方案:

    思路:把爬中的每个方法爬取的数据,都存在硬盘中,存储规则如下:

    file_path:/method_name/

    file_name:MD5_number.txt

    解释:MD5_number是通过方法名和传入的参数计算出来的唯一标识符(搜索一下MD5)这样做的好 处,不需要把数据爬下来之后才能判断是否爬过。

    最后:如果方法爬取目标url的数据时异常,暂不执行该方法。最后多跑几次这个爬虫脚本就可以了!

     

    该爬虫调用三个方法,设计一个通用的方法:根据每个方法的方法名和传入的参数通过MD5计算出唯一的标识符(MD5_numbe)。然后在file_path目录中查找文件名为MD5_numbe的文件,如果file_path中的没有这个文件,则按照存储规则存储数据。如果找到了,不存储到硬盘中,直接返回data,用于下一个方法。

     

    ————————————

    场景结束

    ++++++++++++

    在上面描述的这个场景中,修饰器用在什么地方?

    对了!就是那个通用方法,就需要用修饰器来实现,为什么?一步步的来

     

    python中一切东西都是对象。方法就可以做当作对象来传递!

     

    1.什么是方法?

    传入参数,然后对传入的参数进行一系列操作。

    2.什么是高级方法

    方法接受的参数是方法,仅此而已。

    3.什么是闭包

    当方法1里面有一个方法2,方法2调用的是方法1接收到的参数,最后结果返回方法2。提供这种实现的技术叫做闭包。

     

    说到底修饰器就是一个方法,而传入的参数是方法,并在修饰器中对传入的方法进行一系列的操作(这里用到闭包)。从而实现了,在不修改原方法基础上,增加新的操作。比如上一个场景中的,检验有没有重复爬取数据。

     

    爬中脚本中的代码片段

    func_top是上层页面的处理函数,func_sub是子页面的处理函数,func_bottom是最深层页面的处理函数,func_top会在取到子页面url后遍历调用func_sub,func_sub也是同样。

     1 def func_top(url):
     2     data_dict= {}
     3  
     4     #在页面上获取到子url
     5     sub_urls = xxxx
     6  
     7     data_list = []
     8     for it in sub_urls:
     9         data_list.append(func_sub(it))
    10  
    11     data_dict['data'] = data_list
    12  
    13     return data_dict
    14  
    15 def func_sub(url):
    16     data_dict= {}
    17  
    18     #在页面上获取到子url
    19     bottom_urls = xxxx
    20  
    21     data_list = []
    22     for it in bottom_urls:
    23         data_list.append(func_bottom(it))
    24  
    25     data_dict['data'] = data_list
    26  
    27     return data_dict
    28  
    29 def func_bottom(url):
    30     #获取数据
    31     data = xxxx
    32     return data

     

     

    所以实现方案也就有了:
    定义一个装饰器,如果之前取到数据,就直接取cache(file_path)的数据;如果之前没有取到,那么就从网站拉取,并且存入cache中.

     1 import os
     2 import hashlib
     3  
     4 def deco_args_recent_cache(category='dumps'):
     5     '''
     6     装饰器,返回最新cache的数据
     7     '''
     8     def deco_recent_cache(func):
     9         def func_wrapper(*args, **kargs):
    10             sig = _mk_cache_sig(*args, **kargs)
    11             data = _get_recent_cache(category, func.__name__, sig)
    12             if data is not None:
    13                 return data
    14  
    15             data = func(*args, **kargs)
    16             if data is not None:
    17                 _set_recent_cache(category, func.__name__, sig, data)
    18             return data
    19  
    20         return func_wrapper
    21  
    22     return deco_recent_cache
    23  
    24 def _mk_cache_sig(*args, **kargs):
    25     '''
    26     通过传入参数,生成唯一标识
    27     '''
    28     src_data = repr(args) + repr(kargs)
    29     m = hashlib.md5(src_data)
    30     sig = m.hexdigest()
    31     return sig
    32  
    33 def _get_recent_cache(category, func_name, sig):
    34     full_file_path = '%s/%s/%s' % (category, func_name, sig)
    35     if os.path.isfile(full_file_path):
    36         return eval(file(full_file_path,'r').read())
    37     else:
    38         return None
    39  
    40 def _set_recent_cache(category, func_name, sig, data):
    41     full_dir_path = '%s/%s' % (category, func_name)
    42     if not os.path.isdir(full_dir_path):
    43         os.makedirs(full_dir_path)
    44  
    45     full_file_path = '%s/%s/%s' % (category, func_name, sig)
    46     f = file(full_file_path, 'w+')
    47     f.write(repr(data))
    48     f.close()

    最后,我们只需要在每个func_top,func_sub,func_bottom都加上deco_args_recent_cache这个装饰器即可~~

     

    参考资料:

    浅显理解闭包:https://serholiu.com/python-closures

    修饰器:http://jnotes.googlecode.com/svn/trunk/Notes/NotesOnPythonLearning/Python_decorator.html

  • 相关阅读:
    。。。剑指Offer之——用两个栈实现队列。。。
    。。。剑指Offer之——从尾到头打印链表。。。
    。。。剑指Offer之——替换空格。。。
    。。。剑指Offer之——二维数组中的查找。。。
    。。。归并排序。。。
    。。。快速排序。。。
    。。。冒泡排序。。。
    。。。选择排序。。。
    。。。Shell排序。。。
    Activiti学习笔记2 — HelloWorld
  • 原文地址:https://www.cnblogs.com/xueweihan/p/5063582.html
Copyright © 2011-2022 走看看