python装饰器和偏函数联合运用碰到的问题

2024-06-12 17:38

本文主要是介绍python装饰器和偏函数联合运用碰到的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在写一个爬虫系统,用来爬取学校各个新闻网站的新闻并推送到微信上。

开始,我每个网站写一个爬虫函数,后来发现爬取的过程有许多相同的地方,代码冗余度太大,就想到了用装饰器:

def newsResultProcess(fun):  # 每个新闻爬虫按要求返回5个参数即可,爬取元素的顺序必须是:id、标题、时间@functools.wraps(fun)def wrapper(*args, **kwargs):html, remod, fullurl, imageurl, timestamp = fun(*args, **kwargs)html = u''.join(html.split())result = re.findall(remod, html)result = filter(lambda x: timestamp == x[2], result)return map(lambda x: (fullurl % x[0], x[1], imageurl), result)return wrapper

每个爬虫函数就可以写成这样:

@newsResultProcess
def deanNews():  #教务新闻deannewsurl = 'http://dean.swjtu.edu.cn/servlet/NewsAction?Action=NewsMore'deanfullnewsurl = 'http://dean.swjtu.edu.cn/servlet/NewsView?NewsID=%s'deannewsimage = siteurl + '/static/img/dean.jpg'timestamp = time.strftime(u'%Y-%m-%d')response = urllib2.urlopen(deannewsurl)html = response.read().decode('utf-8')remod = re.compile(ur'<ahref=.*?ID=(.*?)"class.*?title="(.*?)".*?hidden;">(.*?)</div></li>')return html, remod, deanfullnewsurl, deannewsimage, timestamp

这样,每个爬虫函数只要写入每个网页不同的内容就可以了,即链接,时间戳格式,正则表达式,网页编码方式。代码基本上没有什么冗余度了

后来为了提高响应速度,又写了一个装饰器,用来进行缓存:

def newsCache(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):ret = cache.get(fun.__name__)if ret == None:ret = fun(*args, **kwargs)cache.set(fun.__name__, ret, newsexpire)return retreturn wrapper

缓存系统根据函数名来区分各个函数返回的内容


再后来,我发现一个网站可能要爬取多个网页的新闻,这些网页只是链接不同,其他都一样,只用装饰器,仍然会有很高的冗余度。于是,我想到了偏函数,将链接作为参数提供给爬虫函数,用偏函数来固定不同爬虫的链接。就像这样:

@newsCache
@newsResultProcess
def yanghuaCollege( url,image):fullnewsurl = 'http://www.yanghua.net/WebSite/Head/Show.aspx?ID=%s'image = siteurl + imagetimestamp = time.strftime(u'%Y-%m-%d')response = urllib2.urlopen(url)html = response.read().decode('gbk')remod = re.compile(ur'<ahref.*?ID=(\d+).*?>(.*?)\((.*?)\)')return html, remod, fullnewsurl, image, timestampyanghuaCivil = functools.partial(yanghuaCollege, url='', image='/static/img/civil.jpg')

可是这里问题出现了,这时微信端所有新闻页返回的结果都是一样的,又思考了一会我才发现了问题所在:

首先要说装饰器,添加了装饰器后,原来的那个单纯的用def 定义的函数已经不存在了,函数名字__name__也变成了装饰器的名字,不过这里有个补救措施,使用functools里的wraps装饰器,这样外部装饰器返回的函数名仍然不变,这样再根据函数名识别不同的缓存就没有问题。

然后再说偏函数,偏函数就是相当于把一个函数的参数固定,再用一个引用指向这个固定了参数的函数,并不影响原来的函数。但是运用偏函数后,返回的这个函数对象,并不是只在参数上存在区别,实际的情况可以自己打印dir测试。在这里,偏函数返回的函数对象没有了__name__属性(实际上,少了很多属性)。这里我还要仔细研究一下偏函数的工作方式。

最后,当两者放在一起的时候,在这里问题就更严重了,由于装饰器作用之后,函数的一切数性都已确定,所以,缓存装饰器里的函数名,还是内部装饰器返回的,即原函数的名字。于是再加上偏函数的时候,所有返回的偏函数内部用来区分的函数名都是一样的,而且这里不会因为偏函数没有__name__属性发生AttributeError异常,因为缓存里的函数名字在装饰器发生作用的时候是存在的。

最后,我想到的解决办法是,既然函数名已经无法在定义偏函数的时候确定,就只能动态的添加__name__属性了

def newsCache(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):if 'name' in kwargs:fun.__name__ = kwargs['name']  # 由于先进行装饰再使用偏函数,函数名字只能这样动态确定ret = cache.get(fun.__name__)if ret == None:ret = fun(*args, **kwargs)cache.set(fun.__name__, ret, newsexpire)return retreturn wrapperdef newsResultProcess(fun):  # 每个新闻爬虫按要求返回5个参数即可,爬取元素的顺序必须是:id、标题、时间@functools.wraps(fun)def wrapper(*args, **kwargs):html, remod, fullurl, imageurl, timestamp = fun(*args, **kwargs)html = u''.join(html.split())result = re.findall(remod, html)result = filter(lambda x: timestamp == x[2], result)return map(lambda x: (fullurl % x[0], x[1], imageurl), result)return wrapper
<pre name="code" class="python">@newsCache
@newsResultProcess
def yanghuaCollege(urlNo, image, name):url = 'http://www.yanghua.net/WebSite/Head/AcademyBulletin.aspx?CollegeNo=' + urlNofullnewsurl = 'http://www.yanghua.net/WebSite/Head/Show.aspx?ID=%s'image = siteurl + imagetimestamp = time.strftime(u'%Y-%m-%d')response = urllib2.urlopen(url)html = response.read().decode('gbk')remod = re.compile(ur'<ahref.*?ID=(\d+).*?>(.*?)\((.*?)\)')return html, remod, fullnewsurl, image, timestampyanghuaCivil = functools.partial(yanghuaCollege, urlNo='001', image='/static/img/civil.jpg', name='yanghyaCivil')
yanghuaSme = functools.partial(yanghuaCollege, urlNo='002', image='/static/img/sme.jpg', name='yanghyaSme')

到这里,我的爬虫系统终于正常工作了!

python初学,写的不好还请指教!

 

这篇关于python装饰器和偏函数联合运用碰到的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1054888

相关文章

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Python虚拟环境与Conda使用指南分享

《Python虚拟环境与Conda使用指南分享》:本文主要介绍Python虚拟环境与Conda使用指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python 虚拟环境概述1.1 什么是虚拟环境1.2 为什么需要虚拟环境二、Python 内置的虚拟环境工具

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

Python包管理工具核心指令uvx举例详细解析

《Python包管理工具核心指令uvx举例详细解析》:本文主要介绍Python包管理工具核心指令uvx的相关资料,uvx是uv工具链中用于临时运行Python命令行工具的高效执行器,依托Rust实... 目录一、uvx 的定位与核心功能二、uvx 的典型应用场景三、uvx 与传统工具对比四、uvx 的技术实

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

python判断文件是否存在常用的几种方式

《python判断文件是否存在常用的几种方式》在Python中我们在读写文件之前,首先要做的事情就是判断文件是否存在,否则很容易发生错误的情况,:本文主要介绍python判断文件是否存在常用的几种... 目录1. 使用 os.path.exists()2. 使用 os.path.isfile()3. 使用