web-dev-qa-db-ja.com

PythonマルチプロセッシングPicklingError:<type 'function'>をpickleできません

もっと簡単な例ではエラーを再現できないのは残念です。私のコードは複雑すぎて投稿できません。私が普通の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()はすべてトップレベルで定義されています。私はこのパターンでもっと簡単な例を試してみました、そしてそれはうまくいきます。

186
CodeNoob

これは 漬けられるもののリスト です。特に、関数は、それらがモジュールのトップレベルで定義されている場合にのみ選択可能です。

このコードは:

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が選択可能であることに注意してください。

242
unutbu

multiprocessingの代わりにpathos.multiprocesssingを使用します。 pathos.multiprocessingmultiprocessingを使用する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

72
Mike McKerns

他の人が言っているようにmultiprocessingはPythonオブジェクトをpickle化できるワーカープロセスにしか転送できません。 unutbuで説明されているようにコードを再編成できない場合は、次に示すように、データ(特にコードデータ)を転送するためのdillsの拡張ピクル/アンピクル機能を使用できます。

このソリューションは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()
24
rocksportrocker

プロファイラーを使用することで、完璧に機能するコード上で正確にそのエラー出力を生成できることもわかりました。

これはWindows上で行われたことに注意してください(そこではフォークはもう少しエレガントではありません)。

私は走っていた:

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

プロファイリングを削除するとエラーが削除され、プロファイリングを配置するとエラーが回復することがわかりました。コードが機能することを知っていたので、やはり私を軽蔑していました。私は何かがpool.pyを更新したかどうかを確認するためにチェックしていました...それから沈没感を持っていたし、プロファイリングを排除し、それはそれでした。

他の誰かがアーカイブに遭遇した場合のために、アーカイブのためにここに投稿してください。

15

このソリューションでは、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

それは、でこぼこした配列に対しても働きます。

4

@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)
1
shouldsee
Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

このエラーは、非同期ジョブに渡されたモデルオブジェクト内に組み込み関数がある場合にも発生します。

そのため、渡されたモデルオブジェクトには組み込み関数がないことを確認してください。 (私達の場合は、特定のフィールドを追跡するためにモデルの中で Django-model-utilsFieldTracker()関数を使っていました)。これが関連するGitHubの問題への link です。

1
Penkey Suresh