• 关于 pickle模块 的搜索结果

回答

对于序列化最普遍的做法就是使用 pickle 模块。为了将一个对象保存到一个文件中,可以这样做: import pickle data = ... # Some Python object f = open('somefile', 'wb') pickle.dump(data, f) 为了将一个对象转储为一个字符串,可以使用 pickle.dumps() : s = pickle.dumps(data) 为了从字节流中恢复一个对象,使用 pickle.load() 或 pickle.loads() 函数。比如: # Restore from a file f = open('somefile', 'rb') data = pickle.load(f) # Restore from a string data = pickle.loads(s)

哦哦喔 2020-04-17 12:33:22 0 浏览量 回答数 0

回答

str()并非总是可逆的。碰巧的是,通常情况下都是这样。 如果您的数据是JSON可编码的(很可能是),请改用json模块。(如果是这样,请不要使用pickle,但要注意模块文档的警告。) import json def create_random_dictionary(): key = c.create_encryption_dictionary() with open("encryption_key.json", 'w') as file: json.dump(key, file) return key def read_dictionary(): with open('encryption_key.json', 'r') as file: return json.load(file) 回答来源:stackoverflow

is大龙 2020-03-24 21:41:37 0 浏览量 回答数 0

回答

通过使用 multiprocessing.connection 模块可以很容易的实现解释器之间的通信。 下面是一个简单的应答服务器例子: from multiprocessing.connection import Listener import traceback def echo_client(conn): try: while True: msg = conn.recv() conn.send(msg) except EOFError: print('Connection closed') def echo_server(address, authkey): serv = Listener(address, authkey=authkey) while True: try: client = serv.accept() echo_client(client) except Exception: traceback.print_exc() echo_server(('', 25000), authkey=b'peekaboo') 然后客户端连接服务器并发送消息的简单示例: >>> from multiprocessing.connection import Client >>> c = Client(('localhost', 25000), authkey=b'peekaboo') >>> c.send('hello') >>> c.recv() 'hello' >>> c.send(42) >>> c.recv() 42 >>> c.send([1, 2, 3, 4, 5]) >>> c.recv() [1, 2, 3, 4, 5] >>> 跟底层socket不同的是,每个消息会完整保存(每一个通过send()发送的对象能通过recv()来完整接受)。 另外,所有对象会通过pickle序列化。因此,任何兼容pickle的对象都能在此连接上面被发送和接受。

哦哦喔 2020-04-17 17:16:17 0 浏览量 回答数 0

中小企业与商标那些事

企业品牌保护从商标开始,如何挑选一家靠谱的渠道注册商标,解读品牌权益维护的重要节点。

问题

无法在 IDLE 中用 pickle 或 shelve 模块保存变量和打开变量的问题咨询。

问问小秘 2020-01-02 11:55:23 9 浏览量 回答数 1

回答

json 模块提供了一种很简单的方式来编码和解码JSON数据。 其中两个主要的函数是 json.dumps() 和 json.loads() , 要比其他序列化函数库如pickle的接口少得多。 下面演示如何将一个Python数据结构转换为JSON: import json data = { 'name' : 'ACME', 'shares' : 100, 'price' : 542.23 } json_str = json.dumps(data) 下面演示如何将一个JSON编码的字符串转换回一个Python数据结构: data = json.loads(json_str) 如果你要处理的是文件而不是字符串,你可以使用 json.dump() 和 json.load() 来编码和解码JSON数据。例如: # Writing JSON data with open('data.json', 'w') as f: json.dump(data, f) # Reading data back with open('data.json', 'r') as f: data = json.load(f)

哦哦喔 2020-04-17 13:16:12 0 浏览量 回答数 0

问题

模块在调用文件旁边时获取ModuleNotFoundError

is大龙 2020-03-21 11:52:07 4 浏览量 回答数 1

回答

你可以使用函数 types.new_class() 来初始化新的类对象。 你需要做的只是提供类的名字、父类元组、关键字参数,以及一个用成员变量填充类字典的回调函数。例如: # stock.py # Example of making a class manually from parts # Methods def __init__(self, name, shares, price): self.name = name self.shares = shares self.price = price def cost(self): return self.shares * self.price cls_dict = { '__init__' : __init__, 'cost' : cost, } # Make a class import types Stock = types.new_class('Stock', (), {}, lambda ns: ns.update(cls_dict)) Stock.__module__ = __name__ 这种方式会构建一个普通的类对象,并且按照你的期望工作: >>> s = Stock('ACME', 50, 91.1) >>> s <stock.Stock object at 0x1006a9b10> >>> s.cost() 4555.0 >>> 这种方法中,一个比较难理解的地方是在调用完 types.new_class() 对 Stock.__module__ 的赋值。 每次当一个类被定义后,它的 __module__ 属性包含定义它的模块名。 这个名字用于生成 __repr__() 方法的输出。它同样也被用于很多库,比如 pickle 。 因此,为了让你创建的类是“正确”的,你需要确保这个属性也设置正确了。 如果你想创建的类需要一个不同的元类,可以通过 types.new_class() 第三个参数传递给它。例如: >>> import abc >>> Stock = types.new_class('Stock', (), {'metaclass': abc.ABCMeta}, ... lambda ns: ns.update(cls_dict)) ... >>> Stock.__module__ = __name__ >>> Stock <class '__main__.Stock'> >>> type(Stock) <class 'abc.ABCMeta'> >>> 第三个参数还可以包含其他的关键字参数。比如,一个类的定义如下: class Spam(Base, debug=True, typecheck=False): pass 那么可以将其翻译成如下的 new_class() 调用形式: Spam = types.new_class('Spam', (Base,), {'debug': True, 'typecheck': False}, lambda ns: ns.update(cls_dict)) new_class() 第四个参数最神秘,它是一个用来接受类命名空间的映射对象的函数。 通常这是一个普通的字典,但是它实际上是 __prepare__() 方法返回的任意对象,这个在9.14小节已经介绍过了。 这个函数需要使用上面演示的 update() 方法给命名空间增加内容。

哦哦喔 2020-04-17 16:20:15 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

回答

在批评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

问题

根据模糊逻辑结果对CSV文件的搜索结果进行排序

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