2012-02-17 11 views
13

マルチプロセッシングプールに渡す関数にデコレータを使用したいと思います。しかし、コードは "PicklingError:pickle:属性参照__builtin__ .function failed"で失敗します。なぜ私はここで失敗するのか分かりません。私はそれが何か単純だと確信していますが、私はそれを見つけることができません。以下は最小の "実用的な"例です。私はfunctools関数を使用すると、この作業をするには十分だと思っていました。マルチプロセッシングのあるPythonデコレータが失敗する

機能の装飾をコメントアウトすると、問題なく動作します。ここで私が誤解しているのはmultiprocessingのことですか?この仕事をする方法はありますか?

編集:呼び出し可能なクラスのデコレータ関数デコレータの両方を追加した後、それは期待どおりに動作関数デコレータことが判明。呼び出し可能なクラスのデコレータは、引き続き失敗します。それが節約されないようにする呼び出し可能クラスのバージョンについてはどうですか?

import random 
import multiprocessing 
import functools 

class my_decorator_class(object): 
    def __init__(self, target): 
     self.target = target 
     try: 
      functools.update_wrapper(self, target) 
     except: 
      pass 

    def __call__(self, elements): 
     f = [] 
     for element in elements: 
      f.append(self.target([element])[0]) 
     return f 

def my_decorator_function(target): 
    @functools.wraps(target) 
    def inner(elements): 
     f = [] 
     for element in elements: 
      f.append(target([element])[0]) 
     return f 
    return inner 

@my_decorator_function 
def my_func(elements): 
    f = [] 
    for element in elements: 
     f.append(sum(element)) 
    return f 

if __name__ == '__main__': 
    elements = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)] 
    pool = multiprocessing.Pool(processes=4) 
    results = [pool.apply_async(my_func, ([e],)) for e in elements] 
    pool.close() 
    f = [r.get()[0] for r in results] 
    print(f) 
+0

この投稿は、酸洗いされたオブジェクトがトリッキーであることを示すようです:http://gael-varoquaux.info/blog/?p = 120 – Daenyth

+0

はい、そのページも見つかりました。だから私は 'functools'ラッパーを追加しました。しかし、それは何の違いもないようです。私は本当に何が起こっているのか理解できていないことを告白します。 – agarrett

答えて

8

問題は、ピクルスは、ピクルスのすべてを再構成する方法が必要であるということです。漬けことができるもののリストについては、こちらをご覧ください:

http://docs.python.org/library/pickle.html#what-can-be-pickled-and-unpickled

my_funcを酸洗すると、次のコンポーネントが漬けする必要があります

  • my_decorator_classのインスタンス、my_func

    と呼ばれますこれは問題ありません。 Pickleはクラス名を保存し、その内容は__dict__です。 unpicklingすると、クラスを見つけるために名前が使用され、次にインスタンスが作成され、__dict__の内容が入力されます。しかし、__dict__内容は、問題を提示...

  • これはあまりよくないですmy_func.target

    に保存されているオリジナルmy_funcのインスタンス。これはトップレベルの機能であり、通常はこれらを節約することができます。 Pickleは関数の名前を格納します。しかし、問題は、 "my_func"という名前は装飾されていない関数にバインドされなくなりました。これは装飾された関数に束縛されています。つまり、pickleはデコレートされていない関数を参照してオブジェクトを再作成することはできません。悲しいことに、pickleはピクルをしようとしているオブジェクトが常にメイン .my_funcという名前で見つかることを知る方法がありません。

あなたはこのようにそれを変更することができ、それが動作します:あなたはクラスがないときデコレータ機能が動作することを観察している

import random 
import multiprocessing 
import functools 

class my_decorator(object): 
    def __init__(self, target): 
     self.target = target 
     try: 
      functools.update_wrapper(self, target) 
     except: 
      pass 

    def __call__(self, candidates, args): 
     f = [] 
     for candidate in candidates: 
      f.append(self.target([candidate], args)[0]) 
     return f 

def old_my_func(candidates, args): 
    f = [] 
    for c in candidates: 
     f.append(sum(c)) 
    return f 

my_func = my_decorator(old_my_func) 

if __name__ == '__main__': 
    candidates = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)] 
    pool = multiprocessing.Pool(processes=4) 
    results = [pool.apply_async(my_func, ([c], {})) for c in candidates] 
    pool.close() 
    f = [r.get()[0] for r in results] 
    print(f) 

。これは、functools.wrapsが装飾された関数を修飾して、それがラップする関数の名前と他のプロパティを持つからだと思います。 pickleモジュールが理解できる限り、通常のトップレベル関数と区別がつかないので、その名前を保存することでピクルスします。 unpickleにすると、その名前は装飾された関数に束縛され、すべてがうまく機能します。

+0

OK。だから、もし私がこれらのものを漬けたければ、私のデコレータとして呼び出し可能なクラスを使いたいのであれば、 '@'デコレーションアプローチを使うことはできません。あたかもクラスをインスタンス化しているかのように使用しなければなりません。あれは正しいですか? – agarrett

+0

私はそれが正しいと信じています。また、装飾された関数に転送するだけの、飾られていない簡単なトップレベル関数を作成することで、それをまったく避けることもできます。 – Weeble

+0

非常にクリアです。本当にありがとう。 – agarrett