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),
)
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
c
from itertools import repeat
for result in executor.map(f, repeat(a), c):
pass
c
, et peut déballer c
from itertools import izip
for result in executor.map(f, *izip(*c)):
pass
c
, impossible de déballer c
f
pour prendre un seul argument et décompressez l'argument dans la fonction.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.
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))
Où n
est le nombre d'arguments de f
. Ceci est adapté de itertools.tee
.
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)
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.
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
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