我有以下程序:
import string
import itertools
import multiprocessing as mp
def test(word_list):
return list(map(lambda xy: (xy[0], len(list(xy[1]))),
itertools.groupby(sorted(word_list))))
def f(x):
return (x[0], len(list(x[1])))
def test_parallel(word_list):
w = mp.cpu_count()
pool = mp.Pool(w)
return (pool.map(f, itertools.groupby(sorted(word_list))))
def main():
test_list = ["test", "test", "test", "this", "this", "that"]
print(test(test_list))
print(test_parallel(test_list))
return
if __name__ == "__main__":
main()
输出结果为:
[('test', 3), ('that', 1), ('this', 2)]
[('test', 0), ('that', 0), ('this', 1)]
第一行是预期的正确结果。我的问题是,为什么pool.map()没有返回与map()相同的结果?
另外,我知道6项列表不是多处理的最佳选择。这只是我在更大的应用程序中实现时遇到的问题的演示。
我正在使用Python 3.5.1。
答案 0 :(得分:3)
来自https://docs.python.org/3.5/library/itertools.html#itertools.groupby:
返回的组本身就是一个共享底层的迭代器 可以使用groupby()进行迭代。因为源是共享的,所以 groupby()对象是高级的,前一个组不再可见。 因此,如果以后需要该数据,则应将其存储为列表:
groups = [] uniquekeys = [] data = sorted(data, key=keyfunc) for k, g in groupby(data, keyfunc): groups.append(list(g)) # Store group iterator as a list uniquekeys.append(k)
我认为这里的问题是Pool.map
试图切断其输入,并且这样做会迭代groupby
的结果,这会有效地跳过除最后一个之外的所有元素。基。
您的代码的一个修复方法是使用类似[(k, list(v)) for k, v in itertools.groupby(sorted(word_list))]
的内容,但我不知道这对您的实际用例有多适用。
答案 1 :(得分:2)
groupby()
每组返回 iterators ,这些不是独立的来自传入的底层迭代器。您无法独立迭代这些组在平行下;当你访问下一组时,任何前面的组都会提前结束。
pool.map()
将尝试读取所有groupby()
迭代器结果,以将这些结果发送到单独的函数;只是试图获得第二组将导致第一组为空。
只需通过迭代pool.map()
的下一个结果,您就可以在没有groupby()
的情况下看到相同的结果:
>>> from itertools import groupby
>>> word_list = ["test", "test", "test", "this", "this", "that"]
>>> iterator = groupby(sorted(word_list))
>>> first = next(iterator)
>>> next(first[1])
'test'
>>> second = next(iterator)
>>> list(first[1])
[]
第一组的剩余部分是空的'因为已经要求第二组。
这显然是documented:
由于源是共享的,因此当
groupby()
对象提前时,前一个组将不再可见。
你必须实现' 之前将每个组发送到函数:
return pool.map(lambda kg: f((k[0], list(kg[1]))), itertools.groupby(sorted(word_list)))
或
return pool.map(f, (
(key, list(group)) for key, group in itertools.groupby(sorted(word_list))))
其中生成器表达式将实现作为pool.map()
迭代进行处理。