web-dev-qa-db-fra.com

Passer plusieurs paramètres à concurrent.futures.Executor.map?

Le concurrent.futures.Executor.map prend un nombre variable d'itérables à partir desquels la fonction donnée est appelée. Comment dois-je l'appeler si j'ai un générateur qui produit des tuples qui sont normalement déballés en place?

Ce qui suit ne fonctionne pas car chacun des tuples générés est donné comme un argument différent à mapper:

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

Sans le générateur, les arguments souhaités à mapper pourraient ressembler à ceci:

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

Vous devez supprimer le * Lors de l'appel map:

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

Cela appellera f, len(args) fois, où f devrait accepter un paramètre.

Si vous voulez que f accepte deux paramètres, vous pouvez utiliser un appel lambda comme:

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

Un argument qui est répété, un argument dans c

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

Besoin de déballer les éléments de c, et peut déballer c

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

Besoin de déballer les éléments de c, impossible de déballer c

  1. Modifiez f pour prendre un seul argument et décompressez l'argument dans la fonction.
  2. Si chaque élément dans c a un nombre variable de membres, ou si vous appelez f seulement quelques fois:

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

    Il définit une nouvelle fonction qui décompresse chaque élément de c et appelle f. L'utilisation d'un argument par défaut pour f dans le lambda rend f local à l'intérieur du lambda et réduit ainsi le temps de recherche.

  3. Si vous avez un nombre fixe d'arguments et que vous devez appeler f plusieurs fois:

    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))
    

n est le nombre d'arguments de f. Ceci est adapté de itertools.tee .

38
agf

Vous pouvez utiliser le curry pour créer une nouvelle fonction via la méthode partial en Python

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):
    ...

Si vous devez passer plusieurs paramètres, vous pouvez les passer à la méthode partial

func = partial(some_func, a, b, c)
9
Vlad Bezden

Supposons donc que vous ayez une fonction avec prend arguments et tous les 3 arguments sont dynamique et continuez de changer à chaque appel. Par exemple:

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

Pour appeler cela plusieurs fois à l'aide de thread, je créerais d'abord un liste de tuples où chaque Tuple est une version de a, b, c:

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

Pour savoir que la fonction map de concurrent.futures Accepterait le premier argument comme fonction cible et le deuxième argument comme liste d'arguments pour chaque version de la fonction qui sera exécutée. Par conséquent, vous pouvez effectuer un appel comme celui-ci:

for _ in executor.map(multiply, arguments) # Error

Mais cela vous donnera erreur que la fonction attendait 3 arguments but got only 1. Pour résoudre ce problème, nous créons une fonction d'assistance:

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

Maintenant, nous pouvons appeler cette fonction à l'aide de l'exécuteur comme suit:

with ThreadPoolExecutor() as executor:
     for _ in executor.map(helper, arguments):
         pass

Cela devrait vous donner les résultats souhaités.

1
Baqir Khan

Pour ProcessPoolExecutor.map() :

Similaire à map (func, * iterables) sauf:

les itérables sont collectés immédiatement plutôt que paresseusement;

func est exécuté de manière asynchrone et plusieurs appels à func peuvent être effectués simultanément.

Essayez d'exécuter l'extrait de code suivant sous python 3, et vous serez tout à fait clair:

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
0
Tengerye

J'ai vu tant de réponses ici, mais aucune n'est aussi simple que d'utiliser des expressions lambda:

foo (x, y): passer

voulez appeler la méthode ci-dessus 10 fois, avec la même valeur, c'est-à-dire xVal et yVal? avec concurrent.futures.ThreadPoolExecutor () en tant qu'exécuteur:

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