2011-03-25 89 views
8

我多打交道時遇到了一個錯誤酸洗:處理類方法酸洗問題與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 
    """ 

而且我發現一個有用的線索here,該解決方案非常適合這些「self'式instancemethod,但我得到了應用配方問題@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 
""" 

任何改變幾行得到它的類方法的工作方式?

+0

這個帖子可能會有幫助:[http://stackoverflow.com/questions/1914261/](http://stackoverflow.com/questions/1914261/)。 – 2011-03-25 09:59:20

回答

0

而是從_pickle_method返回實際的類的對象,返回可以用來取儲存時導入它,然後做在_unpickle_method

4

我修改了配方,使其與類方法工作的字符串。 這是代碼。

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

以下解決方案現在還可以正確處理類方法。請讓我知道是否仍有缺失。

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)