2016-10-27 7 views
1

マルチプロセッシングコードでいくつかのクロージャを使用しようとしていましたが、だから私は少しのテストでした:結果はPython 3では、マルチプロセッシングのapply_asyncで機能するのはなぜですか、クロージャもラムダもありません。

#!/usr/bin/env python3 

import functools 
from multiprocessing import Pool 

def processing_function(unprocessed_data): 
    return unprocessed_data 

def callback_function(processed_data): 
    print("FUNCTION: " + str(processed_data)) 

def create_processing_closure(initial_data): 
    def processing_function(unprocessed_data): 
     return initial_data + unprocessed_data 
    return processing_function 

def create_callback_closure(): 
    def callback(processed_data): 
     print("CLOSURE: " + str(processed_data)) 
    return callback 

def create_processing_lambda(initial_data): 
    return lambda unprocessed_data: initial_data + unprocessed_data 

def create_callback_lambda(): 
    return lambda processed_data: print("LAMBDA: " + str(processed_data)) 

def processing_partial(unprocessed_data1, unprocessed_data2): 
    return (unprocessed_data1 + unprocessed_data2) 

def callback_partial(initial_data, processed_data): 
    print("PARTIAL: " + str(processed_data)) 

pool = Pool(processes=1) 

print("Testing if they work normally...") 

f1 = processing_function 
f2 = callback_function 

f2(f1(1)) 

f3 = create_processing_closure(1) 
f4 = create_callback_closure() 

f4(f3(1)) 

f5 = create_processing_lambda(1) 
f6 = create_callback_lambda() 

f6(f5(1)) 

f7 = functools.partial(processing_partial, 1) 
f8 = functools.partial(callback_partial, 1) 

f8(f7(1)) 

# bonus round! 
x = 1 
f9 = lambda unprocessed_data: unprocessed_data + x 
f10 = lambda processed_data: print("GLOBAL LAMBDA: " + str(processed_data)) 

f10(f9(1)) 

print("Testing if they work in apply_async...") 

# works 
pool.apply_async(f1, args=(1,), callback=f2) 
# doesn't work 
pool.apply_async(f3, args=(1,), callback=f4) 
# doesn't work 
pool.apply_async(f5, args=(1,), callback=f6) 
# works 
pool.apply_async(f7, args=(1,), callback=f8) 
# doesn't work 
pool.apply_async(f9, args=(1,), callback=f10) 

pool.close() 
pool.join() 

です:

> ./apply_async.py 
Testing if they work normally... 
FUNCTION: 1 
CLOSURE: 2 
LAMBDA: 2 
PARTIAL: 2 
GLOBAL LAMBDA: 2 
Testing if they work in apply_async... 
FUNCTION: 1 
PARTIAL: 2 

誰もがこの奇妙な振る舞いを説明できますか?

+0

FYI:パーシャルはしないでくださいラムダが行うのに対し、np.vectoriseで作業してください! – CMCDragonkai

答えて

2

これらのオブジェクトは別のプロセスに転送できないため、呼び出し可能オブジェクトのpicklingは、オブジェクト自体ではなく、モジュールと名前だけを格納します。

partialは、基礎となる関数オブジェクト(ここでは別のグローバル)を共有するためにのみ機能します。

pickleモジュールドキュメントのWhat can be pickled and unpickled section参照:(defを使用して、ないlambda)モジュールのトップレベルで定義された

  • 機能
  • 内蔵のトップレベルで定義された関数モジュール

[...]

関数(組み込み関数とユーザー定義関数)は、値ではなく、完全修飾名参照によって節約されることに注意してください。 [2]これは、関数が定義されているモジュールの名前とともに、関数名のみが節約されることを意味します。関数のコードも、関数属性のいずれもがピクルされません。したがって、定義モジュールはunpickle環境でインポート可能でなければならず、モジュールには名前付きオブジェクトが含まれていなければなりません。それ以外の場合は例外が発生します。 [3]

multiprocessingProgramming guidelinesに注意してください:

Picklability

プロキシのメソッドの引数はpickle化可能なことを確認してください。化したりunpickle化したり

漬物よりも継承して良い

/

pickle化可能なことをmultiprocessing必要性から開始方法多くの種類forkserver 産卵を使用して、その子プロセスので、それらを使用することができます。しかし、一般的に、パイプやキューを使用して共有オブジェクトを他のプロセスに送信することは避けてください。その代わりに、他の場所で作成された共有リソースへのアクセスを必要とするプロセスが先祖プロセスから継承できるように、プログラムを配置する必要があります。

あなたが直接あなたの呼び出し可能オブジェクトのそれぞれを酸洗しようとした場合、あなたは漬物できるものが正常に呼び出し可能ものと一致することが起こる見ることができるマルチプロセッシングを使用して実行された:

>>> import pickle 
>>> f2(f1(1)) 
FUNCTION: 1 
>>> pickle.dumps([f1, f2]) is not None 
True 
>>> f4(f3(1)) 
CLOSURE: 2 
>>> pickle.dumps([f3, f4]) is not None 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Can't pickle local object 'create_processing_closure.<locals>.processing_function' 
>>> f6(f5(1)) 
LAMBDA: 2 
>>> pickle.dumps([f5, f6]) is not None 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Can't pickle local object 'create_processing_lambda.<locals>.<lambda>' 
>>> f8(f7(1)) 
PARTIAL: 2 
>>> pickle.dumps([f7, f8]) is not None 
True 
>>> f10(f9(1)) 
GLOBAL LAMBDA: 2 
>>> pickle.dumps([f9, f10]) is not None 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
_pickle.PicklingError: Can't pickle <function <lambda> at 0x10994e8c8>: attribute lookup <lambda> on __main__ failed 
+0

最後のパラグラフでpicklingを継承しているのに、明示的にキューを非同期的に適用された関数に渡すのではなく、関数を 'apply_async'に渡してキューのようなグローバル変数を継承させるとしますか? – CMCDragonkai

+0

なぜ部分的に機能するのかを拡張できますか?ドキュメントは、クロージャを返すだけのことと非常に似ていることを示しています。 – CMCDragonkai

+0

@CMCDragonkai:クロージャは、新しく作成された関数オブジェクトのクロージャに変数を格納します(したがって、関数オブジェクト自体が新鮮であるため、反対側で再作成することはできません)。部分は関数オブジェクトへのポインタを持つインスタンスを格納します*ここで*はグローバルであり、したがってもう一方の側では再作成可能です。 –

関連する問題