将多个参数传递给concurrent.futures.Executor.map?

时间:2011-07-22 03:04:52

标签: python concurrency iterator future map-function

concurrent.futures.Executor.map采用可变数量的迭代,从中调用给定的函数。 如果我的生成器生成通常已解压缩的元组,我该如何调用它?

以下方法不起作用,因为每个生成的元组都作为map的不同参数给出:

args = ((a, b) for (a, b) in c)
for result in executor.map(f, *args):
    pass

如果没有生成器,映射的所需参数可能如下所示:

executor.map(
    f,
    (i[0] for i in args),
    (i[1] for i in args),
    ...,
    (i[N] for i in args),
)

8 个答案:

答案 0 :(得分:29)

重复的一个参数,c

中的一个参数
from itertools import repeat
for result in executor.map(f, repeat(a), c):
    pass

需要解压缩c项,并解压c

from itertools import izip
for result in executor.map(f, *izip(*c)):
    pass

需要解压缩c项,无法解包c

  1. 更改f以获取单个参数并在函数中解压缩参数。
  2. 如果c中的每个项目都有可变数量的成员,或者您只是呼叫f几次:

    executor.map(lambda args, f=f: f(*args), c)
    

    它定义了一个新功能,可以从c解包每个项目并调用f。在f中使用lambda的默认参数会使f内的lambda本地化,从而减少查找时间。

  3. 如果您有一定数量的参数,并且需要多次调用f

    from collections import deque
    def itemtee(iterable, n=2):
        def gen(it = iter(iterable), items = deque(), next = next):
            popleft = items.popleft
            extend = items.extend
            while True:
                if not items:
                    extend(next(it))
                yield popleft()
        return [gen()] * n
    
    executor.map(f, *itemtee(c, n))
    
  4. nf的参数数量。这改编自itertools.tee

答案 1 :(得分:23)

您需要移除*电话上的map

args = ((a, b) for b in c)
for result in executor.map(f, args):
    pass

这将调用flen(args)次,其中f应接受一个参数。

如果您希望f接受两个参数,可以使用lambda调用,如:

args = ((a, b) for b in c)
for result in executor.map(lambda p: f(*p), args):   # (*p) does the unpacking part
    pass

答案 2 :(得分:3)

您可以使用currying通过Python中的partial方法创建新函数

from concurrent.futures import ThreadPoolExecutor
from functools import partial


def some_func(param1, param2):
    # some code

# currying some_func with 'a' argument is repeated
func = partial(some_func, a)
with ThreadPoolExecutor() as executor:
    executor.map(func, list_of_args):
    ...

如果您需要传递多个相同的参数,则可以将它们传递给partial方法

func = partial(some_func, a, b, c)

答案 3 :(得分:3)

以下代码片段展示了如何使用 ThreadPoolExecutor 向函数发送多个参数:

import concurrent.futures


def hello(first_name: str, last_name: str) -> None:
    """Prints a friendly hello with first name and last name"""
    print('Hello %s %s!' % (first_name, last_name))


def main() -> None:
    """Examples showing how to use ThreadPoolExecutor and executer.map
    sending multiple arguments to a function"""

    # Example 1: Sending multiple arguments using tuples
    # Define tuples with sequential arguments to be passed to hello()
    args_names = (
        ('Bruce', 'Wayne'),
        ('Clark', 'Kent'),
        ('Diana', 'Prince'),
        ('Barry', 'Allen'),
    )
    with concurrent.futures.ThreadPoolExecutor() as executor:
        # Using lambda, unpacks the tuple (*f) into hello(*args)
        executor.map(lambda f: hello(*f), args_names)

    print()

    # Example 2: Sending multiple arguments using dict with named keys
    # Define dicts with arguments as key names to be passed to hello()
    kwargs_names = (
        {'first_name': 'Bruce', 'last_name': 'Wayne'},
        {'first_name': 'Clark', 'last_name': 'Kent'},
        {'first_name': 'Diana', 'last_name': 'Prince'},
        {'first_name': 'Barry', 'last_name': 'Allen'},
    )
    with concurrent.futures.ThreadPoolExecutor() as executor:
        # Using lambda, unpacks the dict (**f) into hello(**kwargs)
        executor.map(lambda f: hello(**f), kwargs_names)


if __name__ == '__main__':
    main()

答案 4 :(得分:0)

对于ProcessPoolExecutor.map()

  

类似于map(func,* iterables),除了:

     

立即迭代而不是懒惰地收集可迭代对象;

     

func是异步执行的,可能会对func进行了多次调用   同时进行。

尝试在python 3下运行以下代码段,您将很清楚:

from concurrent.futures import ProcessPoolExecutor

def f(a, b):
    print(a+b)

with ProcessPoolExecutor() as pool:
    pool.map(f, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2))

# 0, 2, 4

array = [(i, i) for i in range(3)]
with ProcessPoolExecutor() as pool:
    pool.map(f, *zip(*array))

# 0, 2, 4

答案 5 :(得分:0)

我在这里看到了很多答案,但是没有一个比使用lambda表达式更直接:

foo(x,y):   通过

想用相同的值(即xVal和yVal)调用上述方法10次吗? 将并发.futures.ThreadPoolExecutor()作为执行者:

for _ in executor.map( lambda _: foo(xVal, yVal), range(0, 10)):
    pass

答案 6 :(得分:0)

因此,假设您有一个带有 3个参数的函数,并且所有3个参数都是 dynamic ,并且每次调用时都在不断变化。例如:

def multiply(a,b,c):
    print(a * b * c)

要使用线程多次调用此函数,我首先要创建一个元组列表,其中每个元组都是a,b,c的一个版本:

arguments = [(1,2,3), (4,5,6), (7,8,9), ....]

我们知道concurrent.futures的{​​{1}}函数将第一个参数作为 target函数,将第二个参数作为参数列表对于将要执行的功能的每个版本。因此,您可以这样拨打电话:

map

但这会给您错误,该函数期望for _ in executor.map(multiply, arguments) # Error 。为了解决这个问题,我们创建了一个辅助函数:

3 arguments but got only 1

现在,我们可以使用executor调用此函数,如下所示:

def helper(numbers):
    multiply(numbers[0], numbers[1], numbers[2])

那应该给您想要的结果。

答案 7 :(得分:0)

假设您在下面显示的数据框中有这样的数据,并且您想将第一两列传递给一个函数,该函数将读取图像并预测特征,然后计算差异并返回差异值。

注意:您可以根据您的要求设置任何场景,并且可以分别定义函数。

下面的代码片段将这两列作为参数并传递给线程池机制(也显示进度条)

enter image description here

''' function that will give the difference of two numpy feature matrix'''
def getDifference(image_1_loc, image_2_loc, esp=1e-7):
       arr1 = ''' read 1st image and extract feature '''
       arr2 = ''' read 2nd image and extract feature '''
       diff = arr1.ravel() - arr2.ravel() + esp    
       return diff

'''Using ThreadPoolExecutor from concurrent.futures with multiple argument'''

with ThreadPoolExecutor() as executor:
        result = np.array(
                         list(tqdm(
                                   executor.map(lambda x : function(*x), [(i,j) for i,j in df[['image_1','image_2']].values]),
                               total=len(df)
                                  ) 
                             )
                          )

enter image description here