Fork me on GitHub

[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

posted @ 2015-12-21 15:26  削微寒  阅读(824)  评论(0编辑  收藏  举报