web-dev-qa-db-fra.com

Python multitraitement PicklingError: impossible de décaper <type 'fonction'>

Je suis désolé de ne pouvoir reproduire l'erreur avec un exemple plus simple et mon code est trop compliqué à publier. Si j'exécute le programme dans IPython Shell au lieu du Python standard, les choses se passent bien.

J'ai regardé quelques notes précédentes sur ce problème. Ils ont tous été causés par l'utilisation de pool pour appeler une fonction définie dans une fonction de classe. Mais ce n'est pas le cas pour moi.

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib64/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib64/python2.7/multiprocessing/pool.py", line 313, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

J'apprécierais toute aide.

Mise à jour: La fonction que je pickle est définie au niveau supérieur du module. Bien qu'il appelle une fonction qui contient une fonction imbriquée. c'est-à-dire que f() appelle g() appelle h() qui a une fonction imbriquée i() et que j'appelle pool.apply_async(f). f(), g(), h() sont tous définis au niveau supérieur. J'ai essayé exemple plus simple avec ce modèle et cela fonctionne bien.

198
CodeNoob

Voici un liste de ce qui peut être mariné . En particulier, les fonctions ne peuvent être choisies que si elles sont définies au niveau supérieur d'un module.

Ce morceau de code:

import multiprocessing as mp

class Foo():
    @staticmethod
    def work(self):
        pass

if __== '__main__':   
    pool = mp.Pool()
    foo = Foo()
    pool.apply_async(foo.work)
    pool.close()
    pool.join()

donne une erreur presque identique à celle que vous avez postée:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 315, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Le problème est que les méthodes pool utilisent toutes un mp.SimpleQueue pour passer des tâches aux processus de travail. Tout ce qui passe par mp.SimpleQueue doit être sélectionnable, et foo.work n'est pas picklable car il n'est pas défini au niveau supérieur du module.

Il peut être corrigé en définissant une fonction au niveau supérieur, appelée foo.work():

def work(foo):
    foo.work()

pool.apply_async(work,args=(foo,))

Notez que foo est sélectionnable, car Foo est défini au niveau supérieur et foo.__dict__ est adaptable.

253
unutbu

Je voudrais utiliser pathos.multiprocesssing, au lieu de multiprocessing. pathos.multiprocessing est un fork de multiprocessing utilisant dill. dill peut sérialiser presque tout en python, vous pouvez donc en envoyer beaucoup plus en parallèle. Le fork de pathos permet également de travailler directement avec plusieurs fonctions d’argument, comme vous en avez besoin pour les méthodes de classe.

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> p = Pool(4)
>>> class Test(object):
...   def plus(self, x, y): 
...     return x+y
... 
>>> t = Test()
>>> p.map(t.plus, x, y)
[4, 6, 8, 10]
>>> 
>>> class Foo(object):
...   @staticmethod
...   def work(self, x):
...     return x+1
... 
>>> f = Foo()
>>> p.apipe(f.work, f, 100)
<processing.pool.ApplyResult object at 0x10504f8d0>
>>> res = _
>>> res.get()
101

Obtenez pathos (et si vous aimez, dill) ici: https://github.com/uqfoundation

79
Mike McKerns

Comme d'autres l'ont déjà dit, multiprocessing ne peut transférer que Python objets à des processus de travail pouvant être traités. Si vous ne pouvez pas réorganiser votre code comme décrit par unutbu, vous pouvez utiliser les capacités étendues de pickling/unpickling de dills pour transférer des données (en particulier des données de code), comme indiqué ci-dessous.

Cette solution nécessite uniquement l'installation de dill et pas d'autres bibliothèques que pathos:

import os
from multiprocessing import Pool

import dill


def run_dill_encoded(payload):
    fun, args = dill.loads(payload)
    return fun(*args)


def apply_async(pool, fun, args):
    payload = dill.dumps((fun, args))
    return pool.apply_async(run_dill_encoded, (payload,))


if __== "__main__":

    pool = Pool(processes=5)

    # asyn execution of lambda
    jobs = []
    for i in range(10):
        job = apply_async(pool, lambda a, b: (a, b, a * b), (i, i + 1))
        jobs.append(job)

    for job in jobs:
        print job.get()
    print

    # async execution of static method

    class O(object):

        @staticmethod
        def calc():
            return os.getpid()

    jobs = []
    for i in range(10):
        job = apply_async(pool, O.calc, ())
        jobs.append(job)

    for job in jobs:
        print job.get()
26
rocksportrocker

J'ai constaté que je pouvais également générer exactement cette sortie d'erreur sur un morceau de code parfaitement fonctionnel en essayant d'utiliser le profileur.

Notez que c'était sous Windows (où la fourche est un peu moins élégante).

Je courais:

python -m profile -o output.pstats <script> 

Et constaté que la suppression du profilage supprimait l'erreur et que le profilage le restaurait. Cela me conduisait trop parce que je savais que le code fonctionnait auparavant. Je vérifiais si quelque chose avait mis à jour pool.py ... puis je me sentais couler et j'ai éliminé le profilage.

Publier ici pour les archives au cas où quelqu'un d'autre le rencontrerait.

15
Ezekiel Kruglick

Cette solution ne nécessite que l’installation de dill et pas d’autres librairies en tant que pathos

def apply_packed_function_for_map((dumped_function, item, args, kwargs),):
    """
    Unpack dumped function as target function and call it with arguments.

    :param (dumped_function, item, args, kwargs):
        a Tuple of dumped function and its arguments
    :return:
        result of target function
    """
    target_function = dill.loads(dumped_function)
    res = target_function(item, *args, **kwargs)
    return res


def pack_function_for_map(target_function, items, *args, **kwargs):
    """
    Pack function and arguments to object that can be sent from one
    multiprocessing.Process to another. The main problem is:
        «multiprocessing.Pool.map*» or «apply*»
        cannot use class methods or closures.
    It solves this problem with «dill».
    It works with target function as argument, dumps it («with dill»)
    and returns dumped function with arguments of target function.
    For more performance we dump only target function itself
    and don't dump its arguments.
    How to use (pseudo-code):

        ~>>> import multiprocessing
        ~>>> images = [...]
        ~>>> pool = multiprocessing.Pool(100500)
        ~>>> features = pool.map(
        ~...     *pack_function_for_map(
        ~...         super(Extractor, self).extract_features,
        ~...         images,
        ~...         type='png'
        ~...         **options,
        ~...     )
        ~... )
        ~>>>

    :param target_function:
        function, that you want to execute like  target_function(item, *args, **kwargs).
    :param items:
        list of items for map
    :param args:
        positional arguments for target_function(item, *args, **kwargs)
    :param kwargs:
        named arguments for target_function(item, *args, **kwargs)
    :return: Tuple(function_wrapper, dumped_items)
        It returs a Tuple with
            * function wrapper, that unpack and call target function;
            * list of packed target function and its' arguments.
    """
    dumped_function = dill.dumps(target_function)
    dumped_items = [(dumped_function, item, args, kwargs) for item in items]
    return apply_packed_function_for_map, dumped_items

Cela fonctionne aussi pour les tableaux numpy.

4
Ilia w495 Nikitin

En s’appuyant sur la solution @rocksportrocker, il serait logique d’analyser l’envoi et la réception des résultats.

import dill
import itertools
def run_dill_encoded(payload):
    fun, args = dill.loads(payload)
    res = fun(*args)
    res = dill.dumps(res)
    return res

def dill_map_async(pool, fun, args_list,
                   as_Tuple=True,
                   **kw):
    if as_Tuple:
        args_list = ((x,) for x in args_list)

    it = itertools.izip(
        itertools.cycle([fun]),
        args_list)
    it = itertools.imap(dill.dumps, it)
    return pool.map_async(run_dill_encoded, it, **kw)

if __== '__main__':
    import multiprocessing as mp
    import sys,os
    p = mp.Pool(4)
    res = dill_map_async(p, lambda x:[sys.stdout.write('%s\n'%os.getpid()),x][-1],
                  [lambda x:x+1]*10,)
    res = res.get(timeout=100)
    res = map(dill.loads,res)
    print(res)
1
shouldsee
Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Cette erreur se produit également si une fonction intégrée à l'objet modèle a été transmise au travail asynchrone.

Veillez donc à vérifier que les objets de modèle transmis ne possèdent pas de fonctions intégrées. (Dans notre cas, nous utilisions la fonction FieldTracker() de Django-model-utils à l'intérieur du modèle pour suivre un certain champ). Voici le lien vers le numéro pertinent de GitHub.

1
Penkey Suresh