在Python中使用带有多处理的click.progressbar

sod*_*ate 7 python numpy multiprocessing python-2.7 python-click

我有一个庞大的列表,我需要处理,这需要一些时间,所以我把它分成4件,并用一些功能多处理每件.使用4个内核运行仍然需要一些时间,所以我想我会在函数中添加一些进度条,以便它可以告诉我处理列表时每个处理器的位置.

我的梦想是拥有这样的东西:

erasing close atoms, cpu0  [######..............................]  13%
erasing close atoms, cpu1  [#######.............................]  15%
erasing close atoms, cpu2  [######..............................]  13%
erasing close atoms, cpu3  [######..............................]  14%
Run Code Online (Sandbox Code Playgroud)

每个条随着函数循环的移动而移动.但相反,我得到一个持续的流程:

在此输入图像描述

等等,填满我的终端窗口.

这是调用函数的主要python脚本:

from eraseCloseAtoms import *
from readPDB import *
import multiprocessing as mp
from vectorCalc import *

prot, cell = readPDB('file')
atoms = vectorCalc(cell)

output = mp.Queue()

# setup mp to erase grid atoms that are too close to the protein (dmin = 2.5A)
cpuNum = 4
tasks = len(atoms)
rangeSet = [tasks / cpuNum for i in range(cpuNum)]
for i in range(tasks % cpuNum):
    rangeSet[i] += 1

rangeSet = np.array(rangeSet)

processes = []
for c in range(cpuNum):
    na, nb = (int(np.sum(rangeSet[:c] + 1)), int(np.sum(rangeSet[:c + 1])))
    processes.append(mp.Process(target=eraseCloseAtoms, args=(prot, atoms[na:nb], cell, 2.7, 2.5, output)))

for p in processes:
    p.start()

results = [output.get() for p in processes]

for p in processes:
    p.join()

atomsNew = results[0] + results[1] + results[2] + results[3]
Run Code Online (Sandbox Code Playgroud)

以下是功能eraseCloseAtoms():

import numpy as np
import click


def eraseCloseAtoms(protein, atoms, cell, spacing=2, dmin=1.4, output=None):
    print 'just need to erase close atoms'

    if dmin > spacing:
        print 'the spacing needs to be larger than dmin'
        return

    grid = [int(cell[0] / spacing), int(cell[1] / spacing), int(cell[2] / spacing)]

    selected = list(atoms)
    with click.progressbar(length=len(atoms), label='erasing close atoms') as bar:
        for i, atom in enumerate(atoms):
            bar.update(i)
            erased = False
            coord = np.array(atom[6])

            for ix in [-1, 0, 1]:
                if erased:
                    break
                for iy in [-1, 0, 1]:
                    if erased:
                        break
                    for iz in [-1, 0, 1]:
                        if erased:
                            break
                        for j in protein:
                            protCoord = np.array(protein[int(j)][6])
                            trueDist = getMinDist(protCoord, coord, cell, vectors)
                            if trueDist <= dmin:
                                selected.remove(atom)
                                erased = True
                                break
    if output is None:
        return selected
    else:
        output.put(selected)
Run Code Online (Sandbox Code Playgroud)

Blc*_*ght 5

我在您的代码中看到两个问题。

第一个解释了为什么进度条经常显示100%而不是实际进度。当我认为您希望一步一步更新时,您正在呼叫bar.update(i)这会逐步提高小节的进度i。更好的方法是将Iterable传递给progressbar函数,并使其自动进行更新:

with click.progressbar(atoms, label='erasing close atoms') as bar:
    for atom in bar:
        erased = False
        coord = np.array(atom[6])

        # ...
Run Code Online (Sandbox Code Playgroud)

但是,由于您的代码存在第二个问题,它仍然无法同时用于多个迭代的流程,每个流程都有自己的进度条。该click.progressbar文档指出以下限制:

无需进行任何打印,否则进度条将被无意破坏。

这意味着只要您的进度条之一更新,它就会破坏所有其他活动进度条。

我认为没有简单的解决方案。交互式更新多行控制台输出非常困难(基本上,您需要使用curses或具有OS支持的类似“控制台GUI”库)。该click模块不具备该功能,它只能更新当前行。您最大的希望可能是扩展click.progressbar设计以在列中输出多个条,例如:

CPU1: [######      ] 52%   CPU2: [###        ] 30%    CPU3: [########  ] 84%
Run Code Online (Sandbox Code Playgroud)

这将需要大量的代码来使其工作(特别是当更新来自多个进程时),但这并不是完全不切实际的。


Łuk*_*iak 5

接受的答案表示单击是不可能的,并且需要“数量不多的代码才能使它起作用”。

的确如此,还有另一个具有此功能的模块:tqdm https://github.com/tqdm/tqdm可以满足您的需求。

您可以在文档https://github.com/tqdm/tqdm#nested-progress-bars等中执行嵌套进度条。