• 关于 python调用c和c++模块 的搜索结果

回答

Python具有丰富和强大的库。它常被昵称为胶水语言,能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。比如3D游戏中的图形渲染模块,性能要求特别高,就可以用C/C++重写,而后封装为Python可以调用的扩展类库。这也是人工智能必备知识。在人工智能上使用Python编程语言的优势:1.优质的文档2.平台无关,可以在现在每一个版本上使用3.和其他面向对象编程语言比学习更加简单快速4.Python有许多图像加强库像Python Imaging Libary,VTK和Maya 3D可视化工具包,Numeric Python, Scientific Python和其他很多可用工具可以于数值和科学应用。5.Python的设计非常好,快速,坚固,可移植,可扩展。很明显这些对于人工智能应用来说都是非常重要的因素。6.对于科学用途的广泛编程任务都很有用,无论从小的shell脚本还是整个网站应用。7.最后,它是开源的。可以得到相同的社区支持。希望我提供的答案能够帮助到你,你可以继续留言或者到论坛参与更多的互动,答案满意还请采纳一下,谢谢。

大财主 2019-12-02 01:05:17 0 浏览量 回答数 0

回答

Python具有丰富和强大的库。它常被昵称为胶水语言,能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。比如3D游戏中的图形渲染模块,性能要求特别高,就可以用C/C++重写,而后封装为Python可以调用的扩展类库。这也是人工智能必备知识。在人工智能上使用Python编程语言的优势:1.优质的文档2.平台无关,可以在现在每一个版本上使用3.和其他面向对象编程语言比学习更加简单快速4.Python有许多图像加强库像Python Imaging Libary,VTK和Maya 3D可视化工具包,Numeric Python, Scientific Python和其他很多可用工具可以于数值和科学应用。5.Python的设计非常好,快速,坚固,可移植,可扩展。很明显这些对于人工智能应用来说都是非常重要的因素。6.对于科学用途的广泛编程任务都很有用,无论从小的shell脚本还是整个网站应用。7.最后,它是开源的。可以得到相同的社区支持。希望我提供的答案能够帮助到你,答案满意还请采纳一下,谢谢。另外,你可以继续留言或者到论坛参与更多的互动。

大财主 2019-12-02 01:05:03 0 浏览量 回答数 0

回答

手动定义一个空数组:arr =[],链表数组:a = [1,2,[1,2,3]]。Python, 是一种面向对象的解释型计算机程序设计语言,由荷兰人Guido van Rossum于1989年发明,第一个公开发行版发行于1991年。Python是纯粹的自由软件, 源代码和解释器CPython遵循 GPL(GNU General Public License)协议[2] 。Python语法简洁清晰,特色之一是强制用空白符(white space)作为语句缩进。Python具有丰富和强大的库。它常被昵称为胶水语言,能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。常见的一种应用情形是,使用Python快速生成程序的原型(有时甚至是程序的最终界面),然后对其中[3] 有特别要求的部分,用更合适的语言改写,比如3D游戏中的图形渲染模块,性能要求特别高,就可以用C/C++重写,而后封装为Python可以调用的扩展类库。需要注意的是在您使用扩展类库时可能需要考虑平台问题,某些可能不提供跨平台的实现。

半指温柔乐 2019-12-02 01:09:51 0 浏览量 回答数 0

试用中心

为您提供0门槛上云实践机会,企业用户最高免费12个月

回答

 1. 更加人性化的设计  Python的设计更加人性化,具有快速、坚固、可移植性、可扩展性的特点,十分适合人工智能;开源免费,而且学习简单,很容易实现普及;内置强大的库,可以轻松实现更大强大的功能。  2. 总体的AI库  AIMA:Python实现了从Russell到Norvigs的“人工智能:一种现代的方法”的算法;  pyDatalog:Python中的逻辑编程引擎;  SimpleAI:Python实现在“人工智能:一种现代的方法”这本书中描述过的人工智能的算法,它专注于提供一个易于使用,有良好文档和测试的库;  EasyAI:一个双人AI游戏的python引擎。  3. 机器学习库  PyBrain 一个灵活,简单而有效的针对机器学习任务的算法,它是模块化的Python机器学习库,它也提供了多种预定义好的环境来测试和比较你的算法;  PyML 一个用Python写的双边框架,重点研究SVM和其他内核方法,它支持Linux和Mac OS X;  scikit-learn旨在提供简单而强大的解决方案,可以在不同的上下文中重用:机器学习作为科学和工程的一个多功能工具,它是python的一个模块,集成了经典的机器学习的算法,这些算法是和python科学包紧密联系在一起的;  MDP-Toolkit这是一个Python数据处理的框架,可以很容易的进行扩展。它海收集了有监管和没有监管的学习算饭和其他数据处理单元,可以组合成数据处理序列或者更复杂的前馈网络结构。新算法的实现是简单和直观的。可用的算法是在不断的稳定增加的,包括信号处理方法,流型学习方法,集中分类,概率方法,数据预处理方法等等。  4. 自然语言和文本处理库  NLTK开源的Python模块,语言学数据和文档,用来研究和开发自然语言处理和文本分析,有windows、Mac OSX和Linux版本。  Python具有丰富而强大的库,能够将其他语言制作的各种模块很轻松的联结在一起,对于性能要求高的功能,可以用C/C++进行重写,而后封装成Python可以调用的扩展类库,这是人工智能必备功能,因此,Python编程对人工智能是一门非常有用的语言。

世事皆空 2019-12-02 01:07:33 0 浏览量 回答数 0

回答

全栈是指工程师,架构,前端,后端,安全,应用, 代码。指的是人。Python是一门编程语言。Python是一种面向对象、直译式计算机程序设计语言,由荷兰人Guido van Rossum发明于1989年,1991年发行第一个公开发行版。它常被昵称为胶水语言,它能够很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松地联结在一起。 Python是纯粹的自由软件, 源代码和解释器CPython遵循 GPL协议。语法简洁清晰,特色之一是强制用空白符作为语句缩进。名字来源于一个喜剧,最初设计Python这种语言的人并没有想到Python会在工业和科研上获得如此广泛的使用。Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。自从20世纪90年代初Python语言诞生至今,它逐渐被广泛应用于处理系统管理任务和Web编程。Python已经成为最受欢迎的程序设计语言之一。2011年1月,它被TIOBE编程语言排行榜评为2010年度语言。自从2004年以后,python的使用率是呈线性增长。由于Python语言的简洁、易读以及可扩展性,在国外用Python做科学计算的研究机构日益增多,一些知名大学已经采用Python教授程序设计课程。例如麻省理工学院的计算机科学及编程导论课程就使用Python语言讲授。众多开源的科学计算软件包都提供了Python的调用接口,例如著名的计算机视觉库OpenCV、三维可视化库VTK、医学图像处理库ITK。而Python专用的科学计算扩展库就更多了,科学计算扩展库:NumPy、SciPy和matplotlib,它们分别为Python提供了快速数组处理、数值运算以及绘图功能。因此Python语言及其众多的扩展库所构成的开发环境十分适合工程技术、科研人员处理实验数据、制作图表,甚至开发科学计算应用程序。

ylrf1212 2019-12-02 01:07:37 0 浏览量 回答数 0

问题

2018python技术问答集锦,希望能给喜欢python的同学一些帮助

技术小能手 2019-12-01 19:31:10 2040 浏览量 回答数 2

问题

基础语言百问-Python

薯条酱 2019-12-01 20:12:27 56807 浏览量 回答数 30

回答

在批评Python的讨论中,常常说起Python多线程是多么的难用。还有人对 global interpreter lock(也被亲切的称为“GIL”)指指点点,说它阻碍了Python的多线程程序同时运行。因此,如果你是从其他语言(比如C++或Java)转过来的话,Python线程模块并不会像你想象的那样去运行。必须要说明的是,我们还是可以用Python写出能并发或并行的代码,并且能带来性能的显著提升,只要你能顾及到一些事情。如果你还没看过的话,我建议你看看Eqbal Quran的文章《Ruby中的并发和并行》。 在本文中,我们将会写一个小的Python脚本,用于下载Imgur上最热门的图片。我们将会从一个按顺序下载图片的版本开始做起,即一个一个地下载。在那之前,你得注册一个Imgur上的应用。如果你还没有Imgur账户,请先注册一个。 本文中的脚本在Python3.4.2中测试通过。稍微改一下,应该也能在Python2中运行——urllib是两个版本中区别最大的部分。 1、开始动手让我们从创建一个叫“download.py”的Python模块开始。这个文件包含了获取图片列表以及下载这些图片所需的所有函数。我们将这些功能分成三个单独的函数: get_links download_link setup_download_dir 第三个函数,“setup_download_dir”,用于创建下载的目标目录(如果不存在的话)。 Imgur的API要求HTTP请求能支持带有client ID的“Authorization”头部。你可以从你注册的Imgur应用的面板上找到这个client ID,而响应会以JSON进行编码。我们可以使用Python的标准JSON库去解码。下载图片更简单,你只需要根据它们的URL获取图片,然后写入到一个文件即可。 代码如下: import jsonimport loggingimport osfrom pathlib import Pathfrom urllib.request import urlopen, Request logger = logging.getLogger(__name__) def get_links(client_id): headers = {'Authorization': 'Client-ID {}'.format(client_id)} req = Request('https://api.imgur.com/3/gallery/', headers=headers, method='GET') with urlopen(req) as resp: data = json.loads(resp.readall().decode('utf-8')) return map(lambda item: item['link'], data['data']) def download_link(directory, link): logger.info('Downloading %s', link) download_path = directory / os.path.basename(link) with urlopen(link) as image, download_path.open('wb') as f: f.write(image.readall()) def setup_download_dir(): download_dir = Path('images') if not download_dir.exists(): download_dir.mkdir() return download_dir接下来,你需要写一个模块,利用这些函数去逐个下载图片。我们给它命名为“single.py”。它包含了我们最原始版本的Imgur图片下载器的主要函数。这个模块将会通过环境变量“IMGUR_CLIENT_ID”去获取Imgur的client ID。它将会调用“setup_download_dir”去创建下载目录。最后,使用get_links函数去获取图片的列表,过滤掉所有的GIF和专辑URL,然后用“download_link”去将图片下载并保存在磁盘中。下面是“single.py”的代码: import loggingimport osfrom time import time from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')logging.getLogger('requests').setLevel(logging.CRITICAL)logger = logging.getLogger(__name__) def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] for link in links: download_link(download_dir, link) print('Took {}s'.format(time() - ts)) if name == '__main__': main()注:为了测试方便,上面两段代码可以用如下代码替代演示: coding=utf-8 测试utf-8编码 from time import sleep, timeimport sys, threading reload(sys)sys.setdefaultencoding('utf-8') def getNums(N): return xrange(N) def processNum(num): num_add = num + 1 sleep(1) print str(threading.current_thread()) + ": " + str(num) + " → " + str(num_add) if name == "__main__": t1 = time() for i in getNums(3): processNum(i) print "cost time is: {:.2f}s".format(time() - t1) 结果: <_MainThread(MainThread, started 4436)>: 0 → 1<_MainThread(MainThread, started 4436)>: 1 → 2<_MainThread(MainThread, started 4436)>: 2 → 3cost time is: 3.00s在我的笔记本上,这个脚本花了19.4秒去下载91张图片。请注意这些数字在不同的网络上也会有所不同。19.4秒并不是非常的长,但是如果我们要下载更多的图片怎么办呢?或许是900张而不是90张。平均下载一张图片要0.2秒,900张的话大概需要3分钟。那么9000张图片将会花掉30分钟。好消息是使用了并发或者并行后,我们可以将这个速度显著地提高。 接下来的代码示例将只会显示导入特有模块和新模块的import语句。所有相关的Python脚本都可以在这方便地找到this GitHub repository。 2、使用线程线程是最出名的实现并发和并行的方式之一。操作系统一般提供了线程的特性。线程比进程要小,而且共享同一块内存空间。 在这里,我们将写一个替代“single.py”的新模块。它将创建一个有八个线程的池,加上主线程的话总共就是九个线程。之所以是八个线程,是因为我的电脑有8个CPU内核,而一个工作线程对应一个内核看起来还不错。在实践中,线程的数量是仔细考究的,需要考虑到其他的因素,比如在同一台机器上跑的的其他应用和服务。 下面的脚本几乎跟之前的一样,除了我们现在有个新的类,DownloadWorker,一个Thread类的子类。运行无限循环的run方法已经被重写。在每次迭代时,它调用“self.queue.get()”试图从一个线程安全的队列里获取一个URL。它将会一直堵塞,直到队列中出现一个要处理元素。一旦工作线程从队列中得到一个元素,它将会调用之前脚本中用来下载图片到目录中所用到的“download_link”方法。下载完成之后,工作线程向队列发送任务完成的信号。这非常重要,因为队列一直在跟踪队列中的任务数。如果工作线程没有发出任务完成的信号,“queue.join()”的调用将会令整个主线程都在阻塞状态。 from queue import Queuefrom threading import Thread class DownloadWorker(Thread): def __init__(self, queue): Thread.__init__(self) self.queue = queue def run(self): while True: # Get the work from the queue and expand the tuple # 从队列中获取任务并扩展tuple directory, link = self.queue.get() download_link(directory, link) self.queue.task_done() def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] # Create a queue to communicate with the worker threads queue = Queue() # Create 8 worker threads # 创建八个工作线程 for x in range(8): worker = DownloadWorker(queue) # Setting daemon to True will let the main thread exit even though the workers are blocking # 将daemon设置为True将会使主线程退出,即使worker都阻塞了 worker.daemon = True worker.start() # Put the tasks into the queue as a tuple # 将任务以tuple的形式放入队列中 for link in links: logger.info('Queueing {}'.format(link)) queue.put((download_dir, link)) # Causes the main thread to wait for the queue to finish processing all the tasks # 让主线程等待队列完成所有的任务 queue.join() print('Took {}'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示: coding=utf-8 测试utf-8编码 from Queue import Queuefrom threading import Threadfrom single import *import sys reload(sys)sys.setdefaultencoding('utf-8') class ProcessWorker(Thread): def __init__(self, queue): Thread.__init__(self) self.queue = queue def run(self): while True: # Get the work from the queue num = self.queue.get() processNum(num) self.queue.task_done() def main(): ts = time() nums = getNums(4) # Create a queue to communicate with the worker threads queue = Queue() # Create 4 worker threads # 创建四个工作线程 for x in range(4): worker = ProcessWorker(queue) # Setting daemon to True will let the main thread exit even though the workers are blocking # 将daemon设置为True将会使主线程退出,即使worker都阻塞了 worker.daemon = True worker.start() # Put the tasks into the queue for num in nums: queue.put(num) # Causes the main thread to wait for the queue to finish processing all the tasks # 让主线程等待队列完成所有的任务 queue.join() print("cost time is: {:.2f}s".format(time() - ts)) if name == "__main__": main() 结果: : 3 → 4: 2 → 3: 1 → 2 : 0 → 1cost time is: 1.01s在同一个机器上运行这个脚本,下载时间变成了4.1秒!即比之前的例子快4.7倍。虽然这快了很多,但还是要提一下,由于GIL的缘故,在这个进程中同一时间只有一个线程在运行。因此,这段代码是并发的但不是并行的。而它仍然变快的原因是这是一个IO密集型的任务。进程下载图片时根本毫不费力,而主要的时间都花在了等待网络上。这就是为什么线程可以提供很大的速度提升。每当线程中的一个准备工作时,进程可以不断转换线程。使用Python或其他有GIL的解释型语言中的线程模块实际上会降低性能。如果你的代码执行的是CPU密集型的任务,例如解压gzip文件,使用线程模块将会导致执行时间变长。对于CPU密集型任务和真正的并行执行,我们可以使用多进程(multiprocessing)模块。 官方的Python实现——CPython——带有GIL,但不是所有的Python实现都是这样的。比如,IronPython,使用.NET框架实现的Python就没有GIL,基于Java实现的Jython也同样没有。你可以点这查看现有的Python实现。 3、生成多进程多进程模块比线程模块更易使用,因为我们不需要像线程示例那样新增一个类。我们唯一需要做的改变在主函数中。 为了使用多进程,我们得建立一个多进程池。通过它提供的map方法,我们把URL列表传给池,然后8个新进程就会生成,它们将并行地去下载图片。这就是真正的并行,不过这是有代价的。整个脚本的内存将会被拷贝到各个子进程中。在我们的例子中这不算什么,但是在大型程序中它很容易导致严重的问题。 from functools import partialfrom multiprocessing.pool import Pool def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] download = partial(download_link, download_dir) with Pool(8) as p: p.map(download, links) print('Took {}s'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示: coding=utf-8 测试utf-8编码 from functools import partialfrom multiprocessing.pool import Poolfrom single import *from time import time import sys reload(sys)sys.setdefaultencoding('utf-8') def main(): ts = time() nums = getNums(4) p = Pool(4) p.map(processNum, nums) print("cost time is: {:.2f}s".format(time() - ts)) if name == "__main__": main() 结果: <_MainThread(MainThread, started 6188)>: 0 → 1<_MainThread(MainThread, started 3584)>: 1 → 2<_MainThread(MainThread, started 2572)>: 3 → 4<_MainThread(MainThread, started 4692)>: 2 → 3 cost time is: 1.21s4、分布式任务你已经知道了线程和多进程模块可以给你自己的电脑跑脚本时提供很大的帮助,那么在你想要在不同的机器上执行任务,或者在你需要扩大规模而超过一台机器的的能力范围时,你该怎么办呢?一个很好的使用案例是网络应用的长时间后台任务。如果你有一些很耗时的任务,你不会希望在同一台机器上占用一些其他的应用代码所需要的子进程或线程。这将会使你的应用的性能下降,影响到你的用户们。如果能在另外一台甚至很多台其他的机器上跑这些任务就好了。 Python库RQ非常适用于这类任务。它是一个简单却很强大的库。首先将一个函数和它的参数放入队列中。它将函数调用的表示序列化(pickle),然后将这些表示添加到一个Redis列表中。任务进入队列只是第一步,什么都还没有做。我们至少还需要一个能去监听任务队列的worker(工作线程)。 第一步是在你的电脑上安装和使用Redis服务器,或是拥有一台能正常的使用的Redis服务器的使用权。接着,对于现有的代码只需要一些小小的改动。先创建一个RQ队列的实例并通过redis-py 库传给一台Redis服务器。然后,我们执行“q.enqueue(download_link, download_dir, link)”,而不只是调用“download_link” 。enqueue方法的第一个参数是一个函数,当任务真正执行时,其他的参数或关键字参数将会传给该函数。 最后一步是启动一些worker。RQ提供了方便的脚本,可以在默认队列上运行起worker。只要在终端窗口中执行“rqworker”,就可以开始监听默认队列了。请确认你当前的工作目录与脚本所在的是同一个。如果你想监听别的队列,你可以执行“rqworker queue_name”,然后将会开始执行名为queue_name的队列。RQ的一个很好的点就是,只要你可以连接到Redis,你就可以在任意数量上的机器上跑起任意数量的worker;因此,它可以让你的应用扩展性得到提升。下面是RQ版本的代码: from redis import Redisfrom rq import Queue def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] q = Queue(connection=Redis(host='localhost', port=6379)) for link in links: q.enqueue(download_link, download_dir, link) 然而RQ并不是Python任务队列的唯一解决方案。RQ确实易用并且能在简单的案例中起到很大的作用,但是如果有更高级的需求,我们可以使用其他的解决方案(例如 Celery)。 5、总结如果你的代码是IO密集型的,线程和多进程可以帮到你。多进程比线程更易用,但是消耗更多的内存。如果你的代码是CPU密集型的,多进程就明显是更好的选择——特别是所使用的机器是多核或多CPU的。对于网络应用,在你需要扩展到多台机器上执行任务,RQ是更好的选择。 6、注:关于并发、并行区别与联系并发是指,程序在运行的过程中存在多于一个的执行上下文。这些执行上下文一般对应着不同的调用栈。 在单处理器上,并发程序虽然有多个上下文运行环境,但某一个时刻只有一个任务在运行。 但在多处理器上,因为有了多个执行单元,就可以同时有数个任务在跑。 这种物理上同一时刻有多个任务同时运行的方式就是并行。 和并发相比,并行更加强调多个任务同时在运行。 而且并行还有一个层次问题,比如是指令间的并行还是任务间的并行。

xuning715 2019-12-02 01:10:10 0 浏览量 回答数 0

回答

在批评Python的讨论中,常常说起Python多线程是多么的难用。还有人对 global interpreter lock(也被亲切的称为“GIL”)指指点点,说它阻碍了Python的多线程程序同时运行。因此,如果你是从其他语言(比如C++或Java)转过来的话,Python线程模块并不会像你想象的那样去运行。必须要说明的是,我们还是可以用Python写出能并发或并行的代码,并且能带来性能的显著提升,只要你能顾及到一些事情。如果你还没看过的话,我建议你看看Eqbal Quran的文章《Ruby中的并发和并行》。 在本文中,我们将会写一个小的Python脚本,用于下载Imgur上最热门的图片。我们将会从一个按顺序下载图片的版本开始做起,即一个一个地下载。在那之前,你得注册一个Imgur上的应用。如果你还没有Imgur账户,请先注册一个。 本文中的脚本在Python3.4.2中测试通过。稍微改一下,应该也能在Python2中运行——urllib是两个版本中区别最大的部分。 1、开始动手让我们从创建一个叫“download.py”的Python模块开始。这个文件包含了获取图片列表以及下载这些图片所需的所有函数。我们将这些功能分成三个单独的函数: get_links download_link setup_download_dir 第三个函数,“setup_download_dir”,用于创建下载的目标目录(如果不存在的话)。 Imgur的API要求HTTP请求能支持带有client ID的“Authorization”头部。你可以从你注册的Imgur应用的面板上找到这个client ID,而响应会以JSON进行编码。我们可以使用Python的标准JSON库去解码。下载图片更简单,你只需要根据它们的URL获取图片,然后写入到一个文件即可。 代码如下: import jsonimport loggingimport osfrom pathlib import Pathfrom urllib.request import urlopen, Request logger = logging.getLogger(__name__) def get_links(client_id): headers = {'Authorization': 'Client-ID {}'.format(client_id)} req = Request('https://api.imgur.com/3/gallery/', headers=headers, method='GET') with urlopen(req) as resp: data = json.loads(resp.readall().decode('utf-8')) return map(lambda item: item['link'], data['data']) def download_link(directory, link): logger.info('Downloading %s', link) download_path = directory / os.path.basename(link) with urlopen(link) as image, download_path.open('wb') as f: f.write(image.readall()) def setup_download_dir(): download_dir = Path('images') if not download_dir.exists(): download_dir.mkdir() return download_dir接下来,你需要写一个模块,利用这些函数去逐个下载图片。我们给它命名为“single.py”。它包含了我们最原始版本的Imgur图片下载器的主要函数。这个模块将会通过环境变量“IMGUR_CLIENT_ID”去获取Imgur的client ID。它将会调用“setup_download_dir”去创建下载目录。最后,使用get_links函数去获取图片的列表,过滤掉所有的GIF和专辑URL,然后用“download_link”去将图片下载并保存在磁盘中。下面是“single.py”的代码: import loggingimport osfrom time import time from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')logging.getLogger('requests').setLevel(logging.CRITICAL)logger = logging.getLogger(__name__) def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] for link in links: download_link(download_dir, link) print('Took {}s'.format(time() - ts)) if name == '__main__': main()注:为了测试方便,上面两段代码可以用如下代码替代演示: coding=utf-8 测试utf-8编码 from time import sleep, timeimport sys, threading reload(sys)sys.setdefaultencoding('utf-8') def getNums(N): return xrange(N) def processNum(num): num_add = num + 1 sleep(1) print str(threading.current_thread()) + ": " + str(num) + " → " + str(num_add) if name == "__main__": t1 = time() for i in getNums(3): processNum(i) print "cost time is: {:.2f}s".format(time() - t1) 结果: <_MainThread(MainThread, started 4436)>: 0 → 1<_MainThread(MainThread, started 4436)>: 1 → 2<_MainThread(MainThread, started 4436)>: 2 → 3cost time is: 3.00s在我的笔记本上,这个脚本花了19.4秒去下载91张图片。请注意这些数字在不同的网络上也会有所不同。19.4秒并不是非常的长,但是如果我们要下载更多的图片怎么办呢?或许是900张而不是90张。平均下载一张图片要0.2秒,900张的话大概需要3分钟。那么9000张图片将会花掉30分钟。好消息是使用了并发或者并行后,我们可以将这个速度显著地提高。 接下来的代码示例将只会显示导入特有模块和新模块的import语句。所有相关的Python脚本都可以在这方便地找到this GitHub repository。 2、使用线程线程是最出名的实现并发和并行的方式之一。操作系统一般提供了线程的特性。线程比进程要小,而且共享同一块内存空间。 在这里,我们将写一个替代“single.py”的新模块。它将创建一个有八个线程的池,加上主线程的话总共就是九个线程。之所以是八个线程,是因为我的电脑有8个CPU内核,而一个工作线程对应一个内核看起来还不错。在实践中,线程的数量是仔细考究的,需要考虑到其他的因素,比如在同一台机器上跑的的其他应用和服务。 下面的脚本几乎跟之前的一样,除了我们现在有个新的类,DownloadWorker,一个Thread类的子类。运行无限循环的run方法已经被重写。在每次迭代时,它调用“self.queue.get()”试图从一个线程安全的队列里获取一个URL。它将会一直堵塞,直到队列中出现一个要处理元素。一旦工作线程从队列中得到一个元素,它将会调用之前脚本中用来下载图片到目录中所用到的“download_link”方法。下载完成之后,工作线程向队列发送任务完成的信号。这非常重要,因为队列一直在跟踪队列中的任务数。如果工作线程没有发出任务完成的信号,“queue.join()”的调用将会令整个主线程都在阻塞状态。 from queue import Queuefrom threading import Thread class DownloadWorker(Thread): def __init__(self, queue): Thread.__init__(self) self.queue = queue def run(self): while True: # Get the work from the queue and expand the tuple # 从队列中获取任务并扩展tuple directory, link = self.queue.get() download_link(directory, link) self.queue.task_done() def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] # Create a queue to communicate with the worker threads queue = Queue() # Create 8 worker threads # 创建八个工作线程 for x in range(8): worker = DownloadWorker(queue) # Setting daemon to True will let the main thread exit even though the workers are blocking # 将daemon设置为True将会使主线程退出,即使worker都阻塞了 worker.daemon = True worker.start() # Put the tasks into the queue as a tuple # 将任务以tuple的形式放入队列中 for link in links: logger.info('Queueing {}'.format(link)) queue.put((download_dir, link)) # Causes the main thread to wait for the queue to finish processing all the tasks # 让主线程等待队列完成所有的任务 queue.join() print('Took {}'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示: coding=utf-8 测试utf-8编码 from Queue import Queuefrom threading import Threadfrom single import *import sys reload(sys)sys.setdefaultencoding('utf-8') class ProcessWorker(Thread): def __init__(self, queue): Thread.__init__(self) self.queue = queue def run(self): while True: # Get the work from the queue num = self.queue.get() processNum(num) self.queue.task_done() def main(): ts = time() nums = getNums(4) # Create a queue to communicate with the worker threads queue = Queue() # Create 4 worker threads # 创建四个工作线程 for x in range(4): worker = ProcessWorker(queue) # Setting daemon to True will let the main thread exit even though the workers are blocking # 将daemon设置为True将会使主线程退出,即使worker都阻塞了 worker.daemon = True worker.start() # Put the tasks into the queue for num in nums: queue.put(num) # Causes the main thread to wait for the queue to finish processing all the tasks # 让主线程等待队列完成所有的任务 queue.join() print("cost time is: {:.2f}s".format(time() - ts)) if name == "__main__": main() 结果: : 3 → 4: 2 → 3: 1 → 2 : 0 → 1cost time is: 1.01s在同一个机器上运行这个脚本,下载时间变成了4.1秒!即比之前的例子快4.7倍。虽然这快了很多,但还是要提一下,由于GIL的缘故,在这个进程中同一时间只有一个线程在运行。因此,这段代码是并发的但不是并行的。而它仍然变快的原因是这是一个IO密集型的任务。进程下载图片时根本毫不费力,而主要的时间都花在了等待网络上。这就是为什么线程可以提供很大的速度提升。每当线程中的一个准备工作时,进程可以不断转换线程。使用Python或其他有GIL的解释型语言中的线程模块实际上会降低性能。如果你的代码执行的是CPU密集型的任务,例如解压gzip文件,使用线程模块将会导致执行时间变长。对于CPU密集型任务和真正的并行执行,我们可以使用多进程(multiprocessing)模块。 官方的Python实现——CPython——带有GIL,但不是所有的Python实现都是这样的。比如,IronPython,使用.NET框架实现的Python就没有GIL,基于Java实现的Jython也同样没有。你可以点这查看现有的Python实现。 3、生成多进程多进程模块比线程模块更易使用,因为我们不需要像线程示例那样新增一个类。我们唯一需要做的改变在主函数中。 为了使用多进程,我们得建立一个多进程池。通过它提供的map方法,我们把URL列表传给池,然后8个新进程就会生成,它们将并行地去下载图片。这就是真正的并行,不过这是有代价的。整个脚本的内存将会被拷贝到各个子进程中。在我们的例子中这不算什么,但是在大型程序中它很容易导致严重的问题。 from functools import partialfrom multiprocessing.pool import Pool def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] download = partial(download_link, download_dir) with Pool(8) as p: p.map(download, links) print('Took {}s'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示: coding=utf-8 测试utf-8编码 from functools import partialfrom multiprocessing.pool import Poolfrom single import *from time import time import sys reload(sys)sys.setdefaultencoding('utf-8') def main(): ts = time() nums = getNums(4) p = Pool(4) p.map(processNum, nums) print("cost time is: {:.2f}s".format(time() - ts)) if name == "__main__": main() 结果: <_MainThread(MainThread, started 6188)>: 0 → 1<_MainThread(MainThread, started 3584)>: 1 → 2<_MainThread(MainThread, started 2572)>: 3 → 4<_MainThread(MainThread, started 4692)>: 2 → 3 cost time is: 1.21s4、分布式任务你已经知道了线程和多进程模块可以给你自己的电脑跑脚本时提供很大的帮助,那么在你想要在不同的机器上执行任务,或者在你需要扩大规模而超过一台机器的的能力范围时,你该怎么办呢?一个很好的使用案例是网络应用的长时间后台任务。如果你有一些很耗时的任务,你不会希望在同一台机器上占用一些其他的应用代码所需要的子进程或线程。这将会使你的应用的性能下降,影响到你的用户们。如果能在另外一台甚至很多台其他的机器上跑这些任务就好了。 Python库RQ非常适用于这类任务。它是一个简单却很强大的库。首先将一个函数和它的参数放入队列中。它将函数调用的表示序列化(pickle),然后将这些表示添加到一个Redis列表中。任务进入队列只是第一步,什么都还没有做。我们至少还需要一个能去监听任务队列的worker(工作线程)。 第一步是在你的电脑上安装和使用Redis服务器,或是拥有一台能正常的使用的Redis服务器的使用权。接着,对于现有的代码只需要一些小小的改动。先创建一个RQ队列的实例并通过redis-py 库传给一台Redis服务器。然后,我们执行“q.enqueue(download_link, download_dir, link)”,而不只是调用“download_link” 。enqueue方法的第一个参数是一个函数,当任务真正执行时,其他的参数或关键字参数将会传给该函数。 最后一步是启动一些worker。RQ提供了方便的脚本,可以在默认队列上运行起worker。只要在终端窗口中执行“rqworker”,就可以开始监听默认队列了。请确认你当前的工作目录与脚本所在的是同一个。如果你想监听别的队列,你可以执行“rqworker queue_name”,然后将会开始执行名为queue_name的队列。RQ的一个很好的点就是,只要你可以连接到Redis,你就可以在任意数量上的机器上跑起任意数量的worker;因此,它可以让你的应用扩展性得到提升。下面是RQ版本的代码: from redis import Redisfrom rq import Queue def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] q = Queue(connection=Redis(host='localhost', port=6379)) for link in links: q.enqueue(download_link, download_dir, link) 然而RQ并不是Python任务队列的唯一解决方案。RQ确实易用并且能在简单的案例中起到很大的作用,但是如果有更高级的需求,我们可以使用其他的解决方案(例如 Celery)。 5、总结如果你的代码是IO密集型的,线程和多进程可以帮到你。多进程比线程更易用,但是消耗更多的内存。如果你的代码是CPU密集型的,多进程就明显是更好的选择——特别是所使用的机器是多核或多CPU的。对于网络应用,在你需要扩展到多台机器上执行任务,RQ是更好的选择。 6、注:关于并发、并行区别与联系并发是指,程序在运行的过程中存在多于一个的执行上下文。这些执行上下文一般对应着不同的调用栈。 在单处理器上,并发程序虽然有多个上下文运行环境,但某一个时刻只有一个任务在运行。 但在多处理器上,因为有了多个执行单元,就可以同时有数个任务在跑。 这种物理上同一时刻有多个任务同时运行的方式就是并行。 和并发相比,并行更加强调多个任务同时在运行。 而且并行还有一个层次问题,比如是指令间的并行还是任务间的并行。

xuning715 2019-12-02 01:10:11 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 云栖号物联网 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 云栖号弹性计算 阿里云云栖号 云栖号案例 云栖号直播