web-dev-qa-db-fra.com

Passer plusieurs paramètres à la fonction pool.map () dans Python

J'ai besoin d'un moyen d'utiliser une fonction dans pool.map () qui accepte plus d'un paramètre. Selon ma compréhension, la fonction cible de pool.map () ne peut avoir qu'un paramètre itérable, mais y a-t-il un moyen de faire passer d'autres paramètres également? Dans ce cas, je dois transmettre quelques variables de configuration, telles que mon Lock () et les informations de journalisation à la fonction cible.

J'ai essayé de faire des recherches et je pense que je pourrais peut-être utiliser des fonctions partielles pour le faire fonctionner? Cependant, je ne comprends pas bien comment cela fonctionne. Toute aide serait grandement appréciée! Voici un exemple simple de ce que je veux faire:

def target(items, lock):
    for item in items:
        # Do cool stuff
        if (... some condition here ...):
            lock.acquire()
            # Write to stdout or logfile, etc.
            lock.release()

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    pool.map(target(PASS PARAMS HERE), iterable)
    pool.close()
    pool.join()
48
DJMcCarthy12

Vous pouvez utiliser functools.partial pour cela (comme vous le soupçonniez):

from functools import partial

def target(lock, iterable_item):
    for item in iterable_item:
        # Do cool stuff
        if (... some condition here ...):
            lock.acquire()
            # Write to stdout or logfile, etc.
            lock.release()

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    l = multiprocessing.Lock()
    func = partial(target, l)
    pool.map(func, iterable)
    pool.close()
    pool.join()

Exemple:

def f(a, b, c):
    print("{} {} {}".format(a, b, c))

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    a = "hi"
    b = "there"
    func = partial(f, a, b)
    pool.map(func, iterable)
    pool.close()
    pool.join()

if __== "__main__":
    main()

Sortie:

hi there 1
hi there 2
hi there 3
hi there 4
hi there 5
74
dano

Vous pouvez utiliser une fonction de carte qui autorise plusieurs arguments, comme le fait le fork de multiprocessing trouvé dans pathos.

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> 
>>> def add_and_subtract(x,y):
...   return x+y, x-y
... 
>>> res = Pool().map(add_and_subtract, range(0,20,2), range(-5,5,1))
>>> res
[(-5, 5), (-2, 6), (1, 7), (4, 8), (7, 9), (10, 10), (13, 11), (16, 12), (19, 13), (22, 14)]
>>> Pool().map(add_and_subtract, *Zip(*res))
[(0, -10), (4, -8), (8, -6), (12, -4), (16, -2), (20, 0), (24, 2), (28, 4), (32, 6), (36, 8)]

pathos vous permet d'imbriquer facilement des cartes parallèles hiérarchiques avec plusieurs entrées. Nous pouvons donc étendre notre exemple pour le montrer.

>>> from pathos.multiprocessing import ThreadingPool as TPool
>>> 
>>> res = TPool().amap(add_and_subtract, *Zip(*Pool().map(add_and_subtract, range(0,20,2), range(-5,5,1))))
>>> res.get()
[(0, -10), (4, -8), (8, -6), (12, -4), (16, -2), (20, 0), (24, 2), (28, 4), (32, 6), (36, 8)]

Encore plus amusant est de construire une fonction imbriquée que nous pouvons passer dans le pool. Cela est possible car pathos utilise dill, ce qui permet de sérialiser presque tout en python.

>>> def build_fun_things(f, g):
...   def do_fun_things(x, y):
...     return f(x,y), g(x,y)
...   return do_fun_things
... 
>>> def add(x,y):
...   return x+y
... 
>>> def sub(x,y):
...   return x-y
... 
>>> neato = build_fun_things(add, sub)
>>> 
>>> res = TPool().imap(neato, *Zip(*Pool().map(neato, range(0,20,2), range(-5,5,1))))
>>> list(res)
[(0, -10), (4, -8), (8, -6), (12, -4), (16, -2), (20, 0), (24, 2), (28, 4), (32, 6), (36, 8)]

Si vous ne pouvez pas sortir de la bibliothèque standard, vous devrez le faire d'une autre manière. Votre meilleur pari dans ce cas est d'utiliser multiprocessing.starmap _ comme vu ici: pool.map multitraitement Python pour plusieurs arguments (noté par @Roberto dans les commentaires sur le post de l'OP)

Obtenez pathos ici: https://github.com/uqfoundation

5
Mike McKerns

Si vous n'avez pas accès à functools.partial, Vous pouvez également utiliser une fonction d'emballage pour cela.

def target(lock):
    def wrapped_func(items):
        for item in items:
            # Do cool stuff
            if (... some condition here ...):
                lock.acquire()
                # Write to stdout or logfile, etc.
                lock.release()
    return wrapped_func

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    lck = multiprocessing.Lock()
    pool.map(target(lck), iterable)
    pool.close()
    pool.join()

Cela transforme target() en une fonction qui accepte un verrou (ou les paramètres que vous voulez donner), et renvoie une fonction qui prend seulement une valeur itérable en entrée, mais qui peut toujours utiliser tous vos autres paramètres. C'est ce qui est finalement passé à pool.map(), qui devrait alors s'exécuter sans problème.

0
TheSoundDefense