2011-03-25 3 views
8

j'ai rencontré une erreur de décapage lorsqu'il s'agit de multitraitement:Manipulation de la question de décapage classmethod avec copy_reg

from multiprocessing import Pool 

def test_func(x): 
    return x**2 

class Test: 
    @classmethod 
    def func(cls, x): 
     return x**2 

def mp_run(n, func, args): 
    return Pool(n).map(func, args) 

if __name__ == '__main__': 
    args = range(1,6) 

    print mp_run(5, test_func, args) 
    # [1, 4, 9, 16, 25] 

    print mp_run(5, Test.func, args) 
    """ 
    Exception in thread Thread-3: 
    Traceback (most recent call last): 
     File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner 
     self.run() 
     File "/usr/lib64/python2.6/threading.py", line 484, in run 
     self.__target(*self.__args, **self.__kwargs) 
     File "/usr/lib64/python2.6/multiprocessing/pool.py", line 225, in _handle_tasks 
     put(task) 
    PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed 
    """ 

Et je l'ai trouvé un fil utile here, la solution est parfaite pour ces « instancemethod self' style, mais je a obtenu l'application de la recette problème à @classmethod:

def _pickle_method(method): 
    func_name = method.im_func.__name__ 
    obj = method.im_self 
    cls = method.im_class 
    return _unpickle_method, (func_name, obj, cls) 

def _unpickle_method(func_name, obj, cls): 
    try: 
     for cls in cls.mro(): 
      try: 
       func = cls.__dict__[func_name] 
      except KeyError: 
       pass 
      else: 
       break 
    except AttributeError: 
     func = cls.__dict__[func_name] 
    return func.__get__(obj, cls) 

copy_reg.pickle(MethodType, _pickle_method, _unpickle_method) 
new_func = pickle.loads(pickle.dumps(Test.func)) 
""" 
Traceback (most recent call last): 
File "test3.py", line 45, in <module> 
    new_func = pickle.loads(pickle.dumps(Test.func)) 
File "/usr/lib64/python2.6/pickle.py", line 1366, in dumps 
    Pickler(file, protocol).dump(obj) 
File "/usr/lib64/python2.6/pickle.py", line 224, in dump 
    self.save(obj) 
File "/usr/lib64/python2.6/pickle.py", line 331, in save 
    self.save_reduce(obj=obj, *rv) 
File "/usr/lib64/python2.6/pickle.py", line 401, in save_reduce 
    save(args) 
File "/usr/lib64/python2.6/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
File "/usr/lib64/python2.6/pickle.py", line 562, in save_tuple 
    save(element) 
File "/usr/lib64/python2.6/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
File "/usr/lib64/python2.6/pickle.py", line 748, in save_global 
    (obj, module, name)) 
pickle.PicklingError: Can't pickle <type 'classobj'>: it's not found as __builtin__.classobj 
""" 

Toute façons de modifier quelques lignes pour le faire fonctionner pour classmethod?

+0

Ce poste peut être utile: [http://stackoverflow.com/questions/1914261/](http://stackoverflow.com/questions/1914261/). –

Répondre

0

au lieu de retourner l'objet de classe réelle de _pickle_method, retourner une chaîne qui peut être utilisé pour importer quand unpickling puis le faire dans _unpickle_method

4

J'ai modifié la recette pour le faire fonctionner avec classmethod. Voici le code.

import copy_reg 
import types 

def _pickle_method(method): 
    func_name = method.im_func.__name__ 
    obj = method.im_self 
    cls = method.im_class 
    if func_name.startswith('__') and not func_name.endswith('__'): 
     #deal with mangled names 
     cls_name = cls.__name__.lstrip('_') 
     func_name = '_%s%s' % (cls_name, func_name) 
    return _unpickle_method, (func_name, obj, cls) 

def _unpickle_method(func_name, obj, cls): 
    if obj and func_name in obj.__dict__: 
     cls, obj = obj, None # if func_name is classmethod 
    for cls in cls.__mro__: 
     try: 
      func = cls.__dict__[func_name] 
     except KeyError: 
      pass 
     else: 
      break 
    return func.__get__(obj, cls) 

copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method) 
2

La solution suivante gère désormais correctement les méthodes de classe. S'il vous plaît laissez-moi savoir s'il y a encore quelque chose qui manque.

def _pickle_method(method): 
    """ 
    Pickle methods properly, including class methods. 
    """ 
    func_name = method.im_func.__name__ 
    obj = method.im_self 
    cls = method.im_class 
    if isinstance(cls, type): 
     # handle classmethods differently 
     cls = obj 
     obj = None 
    if func_name.startswith('__') and not func_name.endswith('__'): 
     #deal with mangled names 
     cls_name = cls.__name__.lstrip('_') 
     func_name = '_%s%s' % (cls_name, func_name) 

    return _unpickle_method, (func_name, obj, cls) 

def _unpickle_method(func_name, obj, cls): 
    """ 
    Unpickle methods properly, including class methods. 
    """ 
    if obj is None: 
     return cls.__dict__[func_name].__get__(obj, cls) 
    for cls in cls.__mro__: 
     try: 
      func = cls.__dict__[func_name] 
     except KeyError: 
      pass 
     else: 
      break 
    return func.__get__(obj, cls) 
Questions connexes