質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

並列処理

複数の計算が同時に実行される手法

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1376閲覧

Python:types.ModuleTypeで生成したDynamicModuleを並列処理させようとするとModuleNotFoundErrorになる

m_osa

総合スコア4

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

並列処理

複数の計算が同時に実行される手法

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/05/03 10:41

編集2020/05/05 07:37

前提・実現したいこと

生成したDynamicModuleを並列処理させようとすると、以下のエラーメッセージが発生しました。
このエラーを解消したいと考えております。

発生している問題・エラーメッセージ

Process SpawnPoolWorker-1: Traceback (most recent call last): File "C:\AppData\Local\Programs\Python\Python37\lib\multiprocessing\process.py", line 297, in _bootstrap self.run() File "C:\AppData\Local\Programs\Python\Python37\lib\multiprocessing\process.py", line 99, in run self._target(*self._args, **self._kwargs) File "C:\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 110, in worker task = get() File "C:\AppData\Local\Programs\Python\Python37\lib\multiprocessing\queues.py", line 354, in get return _ForkingPickler.loads(res) **ModuleNotFoundError: No module named 'dynMod'** ・ ・

該当のソースコード

# DynamicModule生成(モジュール名はdynMod) DynamicModule()   # DynamicModuleインポート import dynMod # 並列処理数 pool = Pool(4) # 並列処理 list1 = pool.starmap(dynMod.ParaProc, multi_args)
■再現コード import types import sys def dynamicModule(Param): spam_module = types.ModuleType('spam', 'dynamic generated module') spam_class = """ def calculation(Num1, Num2): # 計算式 result = """ + Param + """ return result if __name__ == "__main__": calculation(1, 2) """ exec(spam_class, spam_module.__dict__) sys.modules['spam'] = spam_module return spam_module if __name__ == "__main__": from multiprocessing import Pool Param = 'Num1 * Num2' # DynamicModule生成 dynamicModule(Param) # DynamicModuleインポート import spam # 並列処理数 pool = Pool(3) # インプットパラメータ multi_args = ([2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7]) # 並列処理 list_result = pool.starmap(spam.calculation, multi_args)

試したこと

・並列処理ではなく、実行すれば正常終了
・import dynModをDynamicModule()で生成するロジックに差し込んだが、同様のエラーが発生
・DynamicModule()で生成するロジックでrunpyをインポートし、再度dynModモジュールを読み込ませようとしたが、同様のエラーが発生

補足情報(FW/ツールのバージョンなど)

Python3.7
Windows10

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

エラーの原因については、子プロセスから実行される位置にモジュールが無い為です。

プロセスベースの並列処理

> このパッケージに含まれる機能を使用するためには、 > 子プロセスから __main__ モジュールをインポートできる必要があります。

解決策1: ThreadPool を使う

python

1from multiprocessing.pool import ThreadPool as Pool

multiprocessing.pool.Pool の代わりに、
multiprocessing.pool.ThreadPool なら、現状のコードを動かす事も可能です。

スレッドの場合は、プロセスの時のような制限はありません。


解決策2: モジュール定義を if __name__ == "__main__": の外に出す。

一般的なケースでは、子プロセスで使われるモジュールや関数は、
if __name__ == "__main__": の外に出す必要があります。

但し、期待された挙動かどうかはわかりません。
dynamicModuleが合計4回呼ばれます。

python

1 2import types 3import sys 4 5def dynamicModule(Param): 6 spam_module = types.ModuleType('spam', 'dynamic generated module') 7 spam_class = """ 8def calculation(Num1, Num2): 9 result = """ + Param + """ 10 return result 11""" 12 exec(spam_class, spam_module.__dict__) 13 sys.modules['spam'] = spam_module 14 15 print("DynamicModule生成") #### XXX: <----- 確認用。計4回呼ばれます。 16 17 return spam_module 18 19Param = 'Num1 * Num2' 20# DynamicModule生成 21dynamicModule(Param) 22# DynamicModuleインポート 23import spam 24 25if __name__ == "__main__": 26 from multiprocessing import Pool 27 28 # インプットパラメータ 29 multi_args = ([2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7]) 30 31 # 並列処理 32 with Pool(3) as pool: 33 list_result = pool.starmap(spam.calculation, multi_args) 34 print(list_result)

解決策3: initializerを指定する。

こちらは2回で済みます。

python

1import types 2import sys 3 4def dynamicModule(Param): 5 spam_module = types.ModuleType('spam', 'dynamic generated module') 6 spam_class = """ 7def calculation(Num1, Num2): 8 result = """ + Param + """ 9 return result 10""" 11 exec(spam_class, spam_module.__dict__) 12 sys.modules['spam'] = spam_module 13 14 print("DynamicModule生成") #### XXX: <----- 確認用。計2回呼ばれます 15 16 return spam_module 17 18 19if __name__ == "__main__": 20 from multiprocessing import Pool 21 22 # インプットパラメータ 23 multi_args = ([2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7]) 24 25 Param = 'Num1 * Num2' 26 # DynamicModule生成 27 dynamicModule(Param) 28 # DynamicModuleインポート 29 import spam 30 31 # 並列処理 32 with Pool(3, dynamicModule, (Param,)) as pool: 33 list_result = pool.starmap(spam.calculation, multi_args) 34 print(list_result)

投稿2020/05/05 08:07

編集2020/05/05 08:12
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m_osa

2020/05/05 09:04

ありがとうございました。3つのいずれの解決策でも問題が解消しました。 DynamicModule生成は1度だけでよいので、解決策1で行おうと思います。
teamikl

2020/05/05 09:30

一点を説明補足で。スレッドの場合はプロセスのような制限がないと書いてしまいましたが、 メインスレッドでの実行を要求されるケースでは、逆にスレッドが実行時に制限になることも有るので、 場合によっては、解決策 (1) ではなく (3) の方法が必要になることも有ります。 (例えばsqlite3でデータベース接続が絡む場合等) 計算のみの場合は、特に影響ありません。 モジュール生成コストもプロセス起動時に一度のみなので、 それ程気にする必要はないと思います。
m_osa

2020/05/05 11:13

ご丁寧にありがとうございました。場合によって使い分けをするようにします。
guest

0

問題が再現できる最小限のコードを提示可能ですか?

エラーログより、multiprocessing.pool.Pool を利用は把握できましたが、
以下の点が不明です。

  • if __name__ == "__main__": の有無
  • ParaProc関数の定義位置 <-- 本来はこちらも重要なのですが
  • 動的なモジュールの実装方法 <-- エラー自体はこちらの内容な気がします

該当のエラー自体は特定できません。(多数原因が考えられます)


モジュール自体は関数を纏めてる入れ物なので、それほど重要ではありません。
(がエラーメッセージは ModuleNotFound なので、そちらの原因はDynamicModuleの内容次第です)

ただし、ドキュメントに説明がある様に
別プロセスに渡す関数自体は import 可能なものである必要があります。
動的にモジュールを構築してる場合、そちらが引っかかりそう。

動的にモジュールを生成した場合のサンプル

提示されたコードを動かす為だけに構成したので、
特に動的にする意味のあるものではありませんが、
大抵の場合、動的な要因を引数にする等、別アプローチで解消した方が良いです。

python

1## 重要 2# https://docs.python.org/ja/3/library/multiprocessing.html 3# > このパッケージに含まれる機能を使用するためには、 4# > 子プロセスから __main__ モジュールをインポートできる必要があります。 5 6def func(n): 7 return n * n 8 9 10def dynamic_module(): 11 from types import ModuleType 12 13 ## ThreadPool の場合はここで関数を定義しても可能 14 # def func(n): 15 # return n * n 16 17 module = ModuleType("DynamicModule") 18 module.func = func 19 20 ## import dynmod でインポートしたい場合 21 import sys 22 sys.modules["dynmod"] = module 23 24 return module 25 26 27dynamic_module() 28import dynmod 29 30 31if __name__ == "__main__": 32 from multiprocessing.pool import Pool, ThreadPool 33 34 with Pool(5) as pool: 35 result = pool.map(dynmod.func, range(10)) 36 print(result)

投稿2020/05/04 14:35

編集2020/05/04 14:36
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m_osa

2020/05/05 07:38 編集

ご回答いただき、ありがとうございます。 問題が再現できるコードは以下になります。 ※理解が乏しいこともあり、上記の内容とは少しロジックを変えております。
teamikl

2020/05/05 07:24

出来れば質問文の編集でお願いします。インデントが解らないので
m_osa

2020/05/05 07:39

申し訳ありません。質問文に再現コードを追記しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問