もっと簡単な例ではエラーを再現できないのは残念です。私のコードは複雑すぎて投稿できません。私が普通のPythonの代わりにIPythonシェルでプログラムを実行するなら、物事はうまくいきます。
私はこの問題に関するいくつかの以前のメモを調べました。これらはすべて、クラス関数内で定義された関数を呼び出すためにプールを使用したことが原因でした。しかし、これは私には当てはまりません。
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
どうぞよろしくお願いします。
更新:私が選ぶ関数はモジュールのトップレベルで定義されています。それは入れ子関数を含む関数を呼び出しますが。つまり、f()
がg()
を呼び出し、h()
がネストされた関数i()
を持ち、私はpool.apply_async(f)
を呼び出しています。 f()
、g()
、h()
はすべてトップレベルで定義されています。私はこのパターンでもっと簡単な例を試してみました、そしてそれはうまくいきます。
これは 漬けられるもののリスト です。特に、関数は、それらがモジュールのトップレベルで定義されている場合にのみ選択可能です。
このコードは:
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()
投稿したものとほぼ同じエラーが発生します。
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
問題は、すべてのpool
メソッドがqueue.Queue
を使ってタスクをワーカープロセスに渡すことです。 queue.Queue
を通過するすべてのものは選択可能でなければならず、foo.work
はモジュールのトップレベルで定義されていないので選択可能ではありません。
foo.work()
を呼び出すトップレベルで関数を定義することで修正できます。
def work(foo):
foo.work()
pool.apply_async(work,args=(foo,))
foo
は最上位レベルで定義され、foo.__dict__
は選択可能であるため、Foo
が選択可能であることに注意してください。
multiprocessing
の代わりにpathos.multiprocesssing
を使用します。 pathos.multiprocessing
はmultiprocessing
を使用するdill
のフォークです。 dill
はpythonのほとんど何でもシリアライズすることができるので、あなたはより多くのものを並列に送ることができます。 pathos
フォークには、クラスメソッドに必要なので、複数の引数関数を直接操作する機能もあります。
>>> 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
ここでpathos
(そしてあなたが好きならdill
)を入手してください: https://github.com/uqfoundation
他の人が言っているようにmultiprocessing
はPythonオブジェクトをpickle化できるワーカープロセスにしか転送できません。 unutbuで説明されているようにコードを再編成できない場合は、次に示すように、データ(特にコードデータ)を転送するためのdill
sの拡張ピクル/アンピクル機能を使用できます。
このソリューションはdill
のインストールのみを必要とし、他のライブラリは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()
プロファイラーを使用することで、完璧に機能するコード上で正確にそのエラー出力を生成できることもわかりました。
これはWindows上で行われたことに注意してください(そこではフォークはもう少しエレガントではありません)。
私は走っていた:
python -m profile -o output.pstats <script>
プロファイリングを削除するとエラーが削除され、プロファイリングを配置するとエラーが回復することがわかりました。コードが機能することを知っていたので、やはり私を軽蔑していました。私は何かがpool.pyを更新したかどうかを確認するためにチェックしていました...それから沈没感を持っていたし、プロファイリングを排除し、それはそれでした。
他の誰かがアーカイブに遭遇した場合のために、アーカイブのためにここに投稿してください。
このソリューションでは、dillをインストールするだけでよく、他のライブラリは必要ありません。
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
それは、でこぼこした配列に対しても働きます。
@rocksportrockerソリューションに基づいて、結果を送信および受信するときにディルすることは理にかなっています。
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)
Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
このエラーは、非同期ジョブに渡されたモデルオブジェクト内に組み込み関数がある場合にも発生します。
そのため、渡されたモデルオブジェクトには組み込み関数がないことを確認してください。 (私達の場合は、特定のフィールドを追跡するためにモデルの中で Django-model-utils のFieldTracker()
関数を使っていました)。これが関連するGitHubの問題への link です。