当前位置: 首页 > 后端技术 > Python

从Python源码证明你的猜测

时间:2023-03-25 22:46:09 Python

看过《Python爬虫开发 从入门到实战》的同学应该对multiprocessing模块不陌生。书中我用这个模块用几行代码实现了一个简单的多线程爬虫:(想自学编程的小伙伴请搜索圈T社区,更多行业相关资料和行业相关免费视频教程.它是完全免费的!)importrequestsfrommultiprocessing.dummyimportPooldefget(url):print(requests.get(url).text,'\n')url_list=['http://exercise.kingname.info/exercise_middleware_ip/1','http://exercise.kingname.info/exercise_middleware_ip/2','http://exercise.kingname.info/exercise_middleware_ip/3','http://exercise.kingname.info/exercise_middleware_ip/4']pool=Pool(3)result=pool.map(get,url_list)运行效果如下图:(没有看过我书的人可能会质疑,multiprocessing不就是多进程模块吗?为什么说是多线程呢?看过书的读者不会有这个疑问,因为我在里面解释了原因thebook)现在,你有一个Function,没有任何参数,但是还是想让他使用多线程,所以模仿上面的代码,你这样写:importrequestsfrommultiprocessing.dummyimportPooldeftest():print('Thefunctionransuccessful!')pool=Pool(3)result=pool.map(test,())运行后发现什么也没有打印出来,也就是说test()函数根本没有运行。如果强行给函数加一个没用的参数,结果又正常了:importrequestsfrommultiprocessing.dummyimportPooldeftest(_):print('Thefunctionrunsuccessful!\n')pool=Pool(3)result=水池。map(test,(0,)*3)运行效果如下图所示。所以你有一种模糊的感觉,如果pool.map的第二个参数是一个空的可迭代对象,那么这个函数就不会运行。(当然用过Python自带的map函数的同学肯定直接知道这一点,但是本文还是以它为例来说明阅读源码的方法。)为了证明这一点,我们打开Python安装目录/lib/multiprocessing/pool.py文件,找到里面的defmap(self,func,iterable,chunksize=None)这行,如下图:(本文以Python3.7.3为例,如果你Python版本不是3.7.3,那么代码可能会有一些差异)从代码可以看出,这里调用了self._map_async(),传入参数,获取返回值后,.调用返回值的get()方法。那么继续看self._map_async()方法:在这个方法中,如果我们传入的iterable对象为空,那么这里的参数iterable为空。所以chunksize=0len(iterable)=0map的第一个参数,函数名传入下面这行代码:task_batches=Pool._get_tasks(func,iterable,chunksize)看静态方法Pool._get_tasks,可以看到:由于这里的参数it是一个空的可迭代对象,大小为0,下面这行代码返回一个空元组:tuple(itertools.islice(it,size))这个生成器就直接结束了,最后一行yield(func,x)根本不会执行。让我们看一下使用MapResult类初始化一个结果对象,然后返回这个对象的代码。然后进入MapResult类,如下图所示:在这个__init__中,可以得到以下参数的值:self._success=Trueself._value=[]#因为[None]*0结果为[]调用self._event.set()返回的结果对象的.get()方法。但是由于MapResult本身没有.get()方法,所以就变成了父类ApplyResult的.get()方法。然后进入ApplyResult,查看.get()方法:由于前面调用了self._event.set(),这里self.ready()的结果为True,而由于上面self._success为True,所以这里直接返回self。_价值。也就是说,返回一个空列表。至此,当pool.map的第二个参数为空的可迭代对象时,所有流程结束。整个过程中,没有调用func的过程。所以原来的函数不会执行。最后说一下为什么这篇文章我们看的是multiprocessing的Pool类中的map方法,而不是multiprocessing.dummy的Pool类中的map方法。这是因为,如果我们打开Python安装路径/Lib/multiprocessing/dummy/__init__.py,可以看到它的Pool实际上返回的是一个ThreadPool对象。而这个对象的代码其实也在Python安装路径/Lib/multiprocessing/pool.py文件中,并且继承自Pool类。所以他们的map方法的代码是完全一样的。