回答此问题可获得 20 贡献值,回答如果被采纳可获得 50 分。
<p>我对decorator不太熟悉,这可能比我第一个decorator项目吃得太多了,但是我想做的是制作一个<code>parallel</code>decorator,它接受一个看似谦逊地应用于单个参数的函数,并自动与<code>multiprocessing</code>一起分发,并将其转换为一个应用于参数列表的函数。在</p>
<p>我正在跟踪前面的问题<a href="https://stackoverflow.com/questions/11726809/python-efficient-workaround-for-multiprocessing-a-function-that-is-a-data-membe">this very helpful answer</a>,这样我就可以成功地pickle类实例方法,并且可以得到类似于答案的示例来正常工作。在</p>
<p>这是我第一次尝试并行decorator(在参考了一些线程装饰器的web点击之后)。在</p>
<pre><code>###########
# Imports #
###########
import types, copy_reg, multiprocessing as mp
import pandas, numpy as np
### End Imports
##################
# Module methods #
##################
# Parallel decorator
def parallel(f):
def executor(*args):
_pool = mp.Pool(2)
_result = _pool.map_async(f, args[1:])
# I used args[1:] because the input will be a
# class instance method, so gotta skip over the self object.
# but it seems like there ought to be a better way...
_pool.close()
_pool.join()
return _result.get()
return executor
### End parallel
def _pickle_method(method):
func_name = method.im_func.__name__
obj = method.im_self
cls = method.im_class
cls_name = ''
if func_name.startswith('__') and not func_name.endswith('__'):
cls_name = cls.__name__.lstrip('_')
if cls_name:
func_name = '_' + cls_name + func_name
return _unpickle_method, (func_name, obj, cls)
### End _pickle_method
def _unpickle_method(func_name, obj, cls):
for cls in cls.mro():
try:
func = cls.__dict__[func_name]
except KeyError:
pass
else:
break
return func.__get__(obj, cls)
### End _unpickle_method
# This call to copy_reg.pickle allows you to pass methods as the first arg to
# mp.Pool methods. If you comment out this line, `pool.map(self.foo, ...)` results in
# PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
# __builtin__.instancemethod failed
copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)
copy_reg.pickle(types.FunctionType, _pickle_method, _unpickle_method)
### End Module methods
##################
# Module classes #
##################
class Foo(object):
def __init__(self, args):
self.my_args = args
### End __init__
def squareArg(self, arg):
return arg**2
### End squareArg
def par_squareArg(self):
p = mp.Pool(2) # Replace 2 with the number of processors.
q = p.map_async(self.squareArg, self.my_args)
p.close()
p.join()
return q.get()
### End par_SquarArg
@parallel
def parSquare(self, num):
return self.squareArg(num)
### End parSquare
### End Foo
### End Module classes
###########
# Testing #
###########
if __name__ == "__main__":
myfoo = Foo([1,2,3,4])
print myfoo.par_squareArg()
print myfoo.parSquare(myfoo.my_args)
### End Testing
</code></pre>
<p>但是当我使用这种方法(愚蠢地尝试用相同的<code>_pickle_method</code>和<code>_unpickle_method</code>)来增强arm pickling函数时,我首先得到一个错误,即,<code>AttributeError: 'function' object has no attribute 'im_func'</code>,但更普遍的错误是函数不能被pickle。在</p>
<p>所以问题是双重的。(1) 我该如何修改decorator,使其获得的<code>f</code>对象是一个类的实例方法,那么它返回的<code>executor</code>也是该类对象的一个实例方法(这样就不会发生不能pickle的事情,因为我可以pickle这些实例方法)?(2)创建附加的<code>_pickle_function</code>和{<cd9>}方法是否更好?我认为Python可以pickle模块级函数,所以如果我的代码没有导致<code>executor</code>成为一个实例方法,那么它看起来应该是一个模块级函数,但是为什么不能对它进行pickle呢?在</p>