我正在研究一个大型脚本,其主要目的是读取许多文件的内容并存储字典中每个元素的编号。如果字典中没有该元素,那么我们将创建一个对象的新实例,然后递增,否则只增加。由于要处理的每个文件本身都很庞大,有时我需要处理100多个文件,我想加快速度并利用Python的多处理模块。这是脚本的大部分简化版本(我用......隐藏了路径,它不是真正的路径):
import multiprocessing as mp
from os import listdir
from os.path import join
manager = mp.Manager()
queue = manager.Queue()
dictionary = manager.dict()
class TestClass:
def __init__(self):
self._number = 0
def increment(self):
self._number += 1
def worker(file):
f = open(file, 'r')
for line in f.readlines():
if line not in dictionary:
dictionary[line] = TestClass()
dictionary[line].increment()
def _list_files():
for f in listdir("..."):
queue.put(join("...", f))
def pool():
_list_files()
_pool = mp.Pool(mp.cpu_count())
for i in range(len(queue)):
_pool.apply(worker, args=(queue.get()))
_pool.close()
_pool.join()
pool()
print(dictionary)
问题是脚本崩溃并显示消息:
AttributeError: Can't get attribute 'TestClass' on <module '__main__' from '.../multiprocessing_test.py'>
有什么方法可以让它发挥作用吗?
我不是那个创建脚本初始版本的人,我只是添加了一些功能。鉴于此,脚本的结构必须保持不变,因为重写它会花费太多时间,即TestClass
,worker
和list_files
不能改变它们的结构(除了所有的东西)连接多处理)
答案 0 :(得分:2)
(好像你之前发过这个问题。)
您的示例代码由于多种原因而无法使用,尤其是...
只是没有做任何有用的事情:
$ python tst.py
Traceback (most recent call last):
File "tst.py", line 38, in <module>
pool()
File "tst.py", line 29, in pool
_list_files()
File "tst.py", line 25, in _list_files
for f in listdir("..."):
OSError: [Errno 2] No such file or directory: '...'
(发布不会运行的代码并不是一个好方法,但 提供MCVE是一个好主意。)所以我修复了:
index 39014ff..1ac9f4a 100644
--- a/tst.py
+++ b/tst.py
@@ -2,6 +2,8 @@ import multiprocessing as mp
from os import listdir
from os.path import join
+DIRPATH = 'inputs'
+
manager = mp.Manager()
queue = manager.Queue()
dictionary = manager.dict()
@@ -22,8 +24,8 @@ def worker(file):
dictionary[line].increment()
def _list_files():
- for f in listdir("..."):
- queue.put(join("...", f))
+ for f in listdir(DIRPATH):
+ queue.put(join(DIRPATH, f))
def pool():
_list_files()
并使用一个示例输入文件创建了一个inputs/
目录:
$ ls inputs
one
$ cat inputs/one
1
one
unum
现在这个例子产生:
$ python tst.py
Traceback (most recent call last):
File "tst.py", line 40, in <module>
pool()
File "tst.py", line 34, in pool
for i in range(len(queue)):
TypeError: object of type 'AutoProxy[Queue]' has no len()
现在,我没有声称这次重写是好,但我继续将其重写为 工作的内容:
import multiprocessing as mp
from os import listdir
from os.path import join
DIRPATH = 'inputs'
class TestClass:
def __repr__(self):
return str(self._number)
def __init__(self):
self._number = 0
def increment(self):
self._number += 1
def worker(dictionary, queue):
while True:
path = queue.get()
if path is None:
return
f = open(path, 'r')
for line in f.readlines():
if line not in dictionary:
dictionary[line] = TestClass()
dictionary[line].increment()
def run_pool():
manager = mp.Manager()
queue = manager.Queue()
dictionary = manager.dict()
nworkers = mp.cpu_count()
pool = mp.Pool(nworkers)
for i in range(nworkers):
pool.apply_async(worker, args=(dictionary, queue))
for f in listdir(DIRPATH):
queue.put(join(DIRPATH, f))
for i in range(nworkers):
queue.put(None)
pool.close()
pool.join()
return dictionary
def main():
dictionary = run_pool()
print(dictionary)
if __name__ == '__main__':
main()
主要区别是:
我删除了所有全局变量。管理器实例,托管队列和托管字典都是run_pool
的本地实例。
在创建nworker
工作人员之后,我将文件的名称放入队列。每个工作程序运行一个循环,读取文件名,直到它读取None
名称,然后返回其(无)结果。
主循环将文件名删除到队列中,以便工作人员可以在完成每个先前文件时将文件名从队列中拉出。要通知所有nworkers
工作人员退出,主循环会将许多None
条目添加到队列中。
run_pool
返回最终(仍然管理的)词典。
当然,我在您的__repr__
对象中添加了TestClass
,以便我们可以查看计数。我还确保代码应该在Windows上运行,方法是将main
驱动程序移动到一个函数中,仅在__name__ == '__main__'
时调用。