<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>「map」タグの記事一覧Python Tech</title>
	<atom:link href="https://tech.nkhn37.net/tag/map/feed/" rel="self" type="application/rss+xml" />
	<link>https://tech.nkhn37.net</link>
	<description>Python学習サイト</description>
	<lastBuildDate>Sun, 11 Jan 2026 02:51:38 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://tech.nkhn37.net/wp-content/uploads/2021/01/cropped-lion-normal-clear-1-32x32.png</url>
	<title>「map」タグの記事一覧Python Tech</title>
	<link>https://tech.nkhn37.net</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>【Python】multiprocessingによるマルチプロセスの基本</title>
		<link>https://tech.nkhn37.net/python-multiprocessing-basics/</link>
					<comments>https://tech.nkhn37.net/python-multiprocessing-basics/#respond</comments>
		
		<dc:creator><![CDATA[naoki-hn]]></dc:creator>
		<pubDate>Sun, 17 Sep 2023 20:00:00 +0000</pubDate>
				<category><![CDATA[multiprocessing]]></category>
		<category><![CDATA[apply]]></category>
		<category><![CDATA[apply_async]]></category>
		<category><![CDATA[array]]></category>
		<category><![CDATA[BaseManager]]></category>
		<category><![CDATA[imap]]></category>
		<category><![CDATA[Manager]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[map_async]]></category>
		<category><![CDATA[Pipe]]></category>
		<category><![CDATA[Pool]]></category>
		<category><![CDATA[Process]]></category>
		<category><![CDATA[Value]]></category>
		<guid isPermaLink="false">https://tech.nkhn37.net/?p=9180</guid>

					<description><![CDATA[Pythonでマルチプロセスを実装するためのmultiprocessingモジュールの使い方について解説します。 Pythonによるマルチプロセス マルチプロセスとは、複数の独立したプロセスを用いてタスクを実行する方法の [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Pythonで<span class="marker"><strong>マルチプロセスを実装するための<code>multiprocessing</code>モジュールの使い方</strong></span>について解説します。</p>



<h2 class="wp-block-heading jinr-heading d--bold">Pythonによるマルチプロセス</h2>



<p class="wp-block-paragraph">マルチプロセスとは、複数の独立したプロセスを用いてタスクを実行する方法のことを言います。Pythonでは、マルチプロセスの実装のための<span class="marker"><strong><code>multiprocessing</code></strong></span>モジュールが提供されています。</p>



<p class="wp-block-paragraph">本記事では、<span class="marker"><strong><code>multiprocessing</code>モジュールを用いたマルチプロセスの実装の基本</strong></span>について紹介します。</p>



<p class="wp-block-paragraph">具体的なモジュールの使い方の説明の前に、まずはPythonにおけるマルチプロセスとマルチスレッドの違いについて簡単に触れておこうと思います。</p>



<h3 class="wp-block-heading jinr-heading d--bold">Pythonにおけるマルチプロセスとマルチスレッド</h3>



<p class="wp-block-paragraph">Pythonには、CPython、Jython、IronPythonなどの複数の実装がありますが、最も広く使用されているのはC言語をベースにしたCPythonです。</p>



<p class="wp-block-paragraph">CPythonにおいては、マルチスレッドを使用することは可能ですが、<span class="marker"><strong>グローバルインタープリタロック（GIL）</strong></span>の存在により、CPUバウンドなタスクにおいてマルチスレッドの利点を最大限に活用できないという制約があります。GILは全てのPython実装に共通する性質ではなく、C言語で実装されたCPython特有のものであることを理解しておくべきです。例えば、Javaで実装されたJythonにはGILは存在しません。</p>



<p class="wp-block-paragraph">GILの存在により、Pythonオブジェクトへのアクセスは一度に一つのスレッドだけに限られます。そのため、PythonでのCPUバウンドな処理では、マルチスレッドを利用しても真の並列実行が難しく、スレッドがシリアル化されて順次実行されるため、高速化されることはほとんどありません。</p>



<p class="wp-block-paragraph">マルチプロセスの場合、各プロセスがGILの影響を受けない独自の実行環境を持つため、リソースをより効果的に活用できます。Pythonを使用して<span class="marker"><strong>マルチコアCPU上でのCPU負荷が高いタスクを実行する場合、マルチプロセスの利用は非常に妥当である</strong></span>と言えます。</p>



<p class="wp-block-paragraph">また、マルチプロセスでは、各プロセスが独立したメモリ空間を持つため、他のプロセスの影響を受けずに動作することが特徴です。対照的に、マルチスレッドではすべてのスレッドが同一のメモリ空間を共有するため、データの安全性に関する注意が必要です。</p>



<p class="wp-block-paragraph">しかし、マルチプロセスを利用する際の課題として、プロセス間のデータの交換が必要になることが挙げられます。Pythonにはこのようなプロセス間の通信を助ける強力なツールが提供されており、以降でも紹介していきます。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--simple-iconbox6 ">
			<i class="jif jin-ifont-v2books" aria-hidden="true"></i>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph">マルチスレッドを実現する <code>threading</code> の使い方は「<a href="https://tech.nkhn37.net/python-threading-multithread/" target="_blank" rel="noreferrer noopener">threadingによるマルチスレッド処理の基本</a>」を参考にしてください。</p>
</div>
		</div></section>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--simple-iconbox6 ">
			<i class="jif jin-ifont-v2books" aria-hidden="true"></i>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph">並行処理や並列処理、I/OバウンドやCPUバウンド、マルチスレッド、マルチプロセスの関係といった内容について「<a href="https://tech.nkhn37.net/pythoni-parallel-process-io-cpu-bound/" target="_blank" rel="noreferrer noopener">並行・並列処理、I/Oバウンド・CPUバウンドを理解する</a>」でまとめていますので参考にしてください。</p>
</div>
		</div></section>



<h2 class="wp-block-heading jinr-heading d--bold">multiprocessingの使い方</h2>



<h3 class="wp-block-heading jinr-heading d--bold">基本的な使い方 Process</h3>



<p class="wp-block-paragraph"><span class="marker"><strong><code>multiprocessing</code></strong></span>モジュールを使って、複数のプロセスを生成し、処理を実行する基本的な使い方を以下の例で見ていきましょう。threadingモジュールを使ったことがある人はほとんど同じであることが分かるかと思います。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import os
import time
from multiprocessing import Process

logging.basicConfig(
    level=logging.DEBUG, format="%(processName)s_%(threadName)s: %(message)s"
)


def myworker1():
    logging.debug(f"start (pid:{os.getpid()})")
    time.sleep(5)
    logging.debug(f"end (pid:{os.getpid()})")


def myworker2():
    logging.debug(f"start (pid:{os.getpid()})")
    time.sleep(5)
    logging.debug(f"end (pid:{os.getpid()})")


def main():
    logging.debug(f"start (pid:{os.getpid()})")

    # プロセスの生成
    process1 = Process(target=myworker1)
    process2 = Process(target=myworker2)

    # プロセスの開始
    process1.start()
    process2.start()

    # プロセスの終了を待機
    process1.join()
    process2.join()

    logging.debug(f"end (pid:{os.getpid()})")


if __name__ == "__main__":
    main()
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess_MainThread: start (pid:27024)
Process-1_MainThread: start (pid:28944)
Process-2_MainThread: start (pid:18884)
Process-2_MainThread: end (pid:18884)
Process-1_MainThread: end (pid:28944)
MainProcess_MainThread: end (pid:27024)</pre>



<p class="wp-block-paragraph">上記例は、<code>myworker1</code>と<code>myworker2</code>という関数を別のプロセスで並列処理するプログラムとなっています。実行してみていただくとProcess-1とProcess-2というプロセスが動作し、それぞれのメインスレッド(MainThread)で処理が動作していることが分かります。以下のイメージ図のような形です。</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="898" height="252" src="https://tech.nkhn37.net/wp-content/uploads/2023/09/image-4.png" alt="multiprocessing Process" class="wp-image-9194" srcset="https://tech.nkhn37.net/wp-content/uploads/2023/09/image-4.png 898w, https://tech.nkhn37.net/wp-content/uploads/2023/09/image-4-300x84.png 300w, https://tech.nkhn37.net/wp-content/uploads/2023/09/image-4-768x216.png 768w" sizes="(max-width: 898px) 100vw, 898px" /></figure>



<p class="wp-block-paragraph"><code>os.getpid</code>でProcess ID(PID)を取得し表示していますが、それぞれ異なっていることからも別プロセスであることが把握できます。</p>



<p class="wp-block-paragraph">プロセスの生成は、<code>multiprocessing</code>モジュールの<span class="marker"><strong><code>Process</code></strong></span>を使います。以下のように<code>target</code>引数に各プロセスで実行する関数を渡します。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # プロセスの生成
    process1 = Process(target=myworker1)
    process2 = Process(target=myworker2)</pre>



<p class="wp-block-paragraph">各プロセスの終了を待機するのが<span class="marker"><strong><code>join</code></strong></span>メソッドです。プロセスが終了したら、<code>join</code>より下のコードが実行されます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # プロセスの終了を待機
    process1.join()
    process2.join()</pre>



<p class="wp-block-paragraph">プロセスの出力では、以下の部分で定義しているように<code>logging</code>モジュールを使用しています。<code>logging</code>の<code>format</code>で<code>processName</code>や<code>threadName</code>を指定すると分かりやすく表示できます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">logging.basicConfig(
    level=logging.DEBUG, format="%(processName)s_%(threadName)s: %(message)s"
)</pre>



<p class="wp-block-paragraph">今回は、それぞれでプロセスでMainThreadが動いていることを示すために<code>threadName</code>を表示しましたが、以降の例では煩雑になるので<code>processName</code>のみ表示にします。</p>



<p class="wp-block-paragraph">なお、<code>logging</code>については「<a rel="noreferrer noopener" href="https://tech.nkhn37.net/python-logging-basic/" target="_blank">loggingの基本的な使い方</a>」でまとめていますので興味があれば参考にしてください。</p>



<p class="wp-block-paragraph">上記のように<code>Process</code>を使うことで簡単にマルチプロセスの実装をすることができます。</p>



<h4 class="wp-block-heading jinr-heading d--bold">プロセス名称をつけたい場合</h4>



<p class="wp-block-paragraph">上記例では、プロセス名称(<code>processName</code>)が「Process-1」「Process-2」というようになっていました。プロセスに個別に名称をつけたい場合には、<code>Process</code>生成時に以下のように<span class="marker"><strong><code>name</code></strong></span>引数で指定します。（変更のない部分は省略します。）</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...(省略)...

    # プロセスの生成
    process1 = Process(target=myworker1, name="myworker1_process")
    process2 = Process(target=myworker2, name="myworker2_process")

...(省略)...</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
myworker1_process: start
myworker2_process: start
myworker1_process: end
myworker2_process: end
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>name</code>引数で指定することで、<code>logging</code>で出力している<code>processName</code>の部分は指定した名称に変わります。</p>



<h4 class="wp-block-heading jinr-heading d--bold">プロセスで動作する関数への引数の渡し方</h4>



<p class="wp-block-paragraph">プロセスで動作する関数へ引数を渡したい場合には、<code>Process</code>の生成時に以下のように<code>args</code>引数や<code>kwargs</code>引数を使用します。（変更のない部分は省略します）</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...(省略)...

def myworker1(x: int, y: int):
    logging.debug("start")
    logging.debug(f"x: {x}, y: {y}")
    time.sleep(5)
    logging.debug("end")

...(省略)...

    # プロセスの生成
    process1 = Process(target=myworker1, args=(10,), kwargs={"y": 20})

...(省略)...</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
Process-1: start
Process-1: x: 10, y: 20
Process-2: start
Process-1: end
Process-2: end
MainProcess: end</pre>



<p class="wp-block-paragraph">上記例では、<code>myworker1</code>の方の関数に引数として<code>int</code>の<code>x</code>, <code>y</code>があります。この関数に引数を渡す場合には、<code>args</code>にタプルで位置引数で指定するか、<code>kwargs</code>に辞書でキーワード引数として指定することができます。<code>args</code>はタプルなので、1つ引数を渡す場合は上記の<code>(10,)</code>のようにカンマ(,)がいるので注意しましょう。</p>



<p class="wp-block-paragraph">実行結果を見てもわかるようにProcess-1の方では、メインプロセスから引数として渡した情報を表示できていることが分かります。</p>



<h4 class="wp-block-heading jinr-heading d--bold">マルチスレッドとマルチプロセスのメモリの扱いの違い</h4>



<p class="wp-block-paragraph">冒頭でマルチプロセスは各プロセスが独立したメモリ空間を持つため、他のプロセスの影響を受けずに動作するということを説明しました。対照的に、マルチスレッドではすべてのスレッドが同一のメモリ空間を共有します。</p>



<p class="wp-block-paragraph">このことを簡単なプログラムで確認してみたいと思います。</p>



<h5 class="wp-block-heading jinr-heading d--bold">マルチプロセスの場合</h5>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
from multiprocessing import Process

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")


def myworker(data: dict):
    logging.debug("start")
    data["x"] += 1
    logging.debug(f"end: {data}, {id(data)}")


def main():
    logging.debug("start")

    # データを用意
    data = {"x": 0}

    # プロセスを生成
    process1 = Process(target=myworker, args=(data,))
    process2 = Process(target=myworker, args=(data,))

    # プロセスの開始
    process1.start()
    process2.start()

    # プロセスの終了を待機
    process1.join()
    process2.join()

    logging.debug(f"end: {data}, {id(data)}")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
Process-1: start
Process-2: start
Process-1: end: {'x': 1}, 2324205073536
Process-2: end: {'x': 1}, 1441800731136
MainProcess: end: {'x': 0}, 3244999688960</pre>



<p class="wp-block-paragraph">上記例では、<code>data</code>という辞書を用意して、Process-1、Process-2に渡しています。この時、対象の辞書の<code>id</code>を確認していますが、マルチプロセスでは、MainProcess、Process-1、Process-2それぞれで<code>id</code>が異なっていることが分かります。</p>



<p class="wp-block-paragraph">Process-1やProcess-2は、開始時点でMainProcessから子プロセスとして生成されます。このような処理をフォーク(fork)と言います。この時点で<code>data</code>の辞書は<code>{'x': 0}</code>で各プロセスごとでコピーされるような形となるため、各プロセスでは1が加算されて<code>{'x': 1}</code>となっていますが、MainProcessでは<code>{'x':0}</code>のままになります。</p>



<p class="wp-block-paragraph">このように、マルチプロセスでは各プロセスで独立したメモリ空間を扱います。</p>



<h5 class="wp-block-heading jinr-heading d--bold">マルチスレッドの場合</h5>



<p class="wp-block-paragraph">以下は上記と全く同じコードを、マルチスレッドで実装した場合です。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
from threading import Thread

logging.basicConfig(level=logging.DEBUG, format="%(threadName)s: %(message)s")


def myworker(data: dict):
    logging.debug("start")
    data["x"] += 1
    logging.debug(f"end: {data}, {id(data)}")


def main():
    logging.debug("start")

    # データを用意
    data = {"x": 0}

    # スレッドを生成
    thread1 = Thread(target=myworker, args=(data,))
    thread2 = Thread(target=myworker, args=(data,))

    # スレッドの開始
    thread1.start()
    thread2.start()

    # スレッドの終了を待機
    thread1.join()
    thread2.join()

    logging.debug(f"end: {data}, {id(data)}")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainThread: start
Thread-1 (myworker): start
Thread-1 (myworker): end: {'x': 1}, 3138390283776
Thread-2 (myworker): start
Thread-2 (myworker): end: {'x': 2}, 3138390283776
MainThread: end: {'x': 2}, 3138390283776</pre>



<p class="wp-block-paragraph">マルチスレッドでも同じように実装していますが、MainThread、Thread-1、Thread-2で対象の辞書の<code>id</code>は全く同じになっていることが分かります。このようにマルチスレッドでは各スレッドが同じメモリ領域を参照します。</p>



<p class="wp-block-paragraph">実際に上記のプログラムはスレッドセーフではありません。特に<code>data["x"] +=1</code>という部分は、複数のスレッドから同時にアクセスされると不正な結果を生じる可能性があります。安全にデータの更新を行うには<code>Lock</code>等を使用して排他制御を行うのが適切です。</p>



<p class="wp-block-paragraph">上記で見たようにマルチプロセスとマルチスレッドのメモリの扱いの違いはよく理解しておくようにしましょう。</p>



<h3 class="wp-block-heading jinr-heading d--bold">プロセスプール Pool</h3>



<p class="wp-block-paragraph">マルチプロセスは、各プロセスが独立したリソースを使用するため子プロセスを大量に生成してしまうことは避けるべきです。マルチプロセスを用いたアプリケーションでは、<span class="marker"><strong>プロセスプール</strong></span>を構築してリソースの利用を制限するのが良い方法です。</p>



<p class="wp-block-paragraph"><code>multiprocessing</code>モジュールでは、<span class="marker"><strong><code>Pool</code></strong></span>クラスというプロセスプールのための枠組みを提供してくれています。これは、<code>threading</code>モジュールには含まれておらず、この点は<code>multiprocessing</code>の方が便利だと感じる点です。<code>Pool</code>クラスが、複数のプロセスの管理を簡単にしてくれます。</p>



<p class="wp-block-paragraph"><code>Pool</code>は、以下のように<code>with</code>句で使用します。ここでは構成を説明のみとし、具体的な処理は省略しています。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker():
    ...(マルチプロセスで動作させる処理を記載)...


def main():
    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        ...(処理を記載)...


if __name__ == "__main__":
    main()</pre>



<p class="wp-block-paragraph">上記例では、プロセスプールのサイズを3にしているので、プロセスは同時に3つまで起動します。(処理の記載)の部分では、使用できる便利なメソッドがいくつもあります。以降ではそれらのメソッドを使用したプロセスプールの実装例を紹介していきます。</p>



<h4 class="wp-block-heading jinr-heading d--bold">プールでのプロセス実行 apply_async</h4>



<p class="wp-block-paragraph"><code>Pool</code>を使ったプロセスプールでプロセスを実行する場合には、以下のように<span class="marker"><strong><code>apply_async</code></strong></span>メソッドが使用できます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import time
from multiprocessing import Pool

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")

# プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker(x: int):
    logging.debug(f"start")
    time.sleep(5)
    logging.debug("end")

    return x


def main():
    logging.debug("start")

    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        process1 = pool.apply_async(func=myworker, args=(10,))
        process2 = pool.apply_async(func=myworker, args=(20,))
        process3 = pool.apply_async(func=myworker, args=(30,))
        process4 = pool.apply_async(func=myworker, args=(40,))
        process5 = pool.apply_async(func=myworker, args=(50,))

        logging.debug("execute async")

        # 実行結果を取得
        logging.debug(process1.get())
        logging.debug(process2.get())
        logging.debug(process3.get())
        logging.debug(process4.get())
        logging.debug(process5.get())

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: execute async
SpawnPoolWorker-1: start
SpawnPoolWorker-3: start
SpawnPoolWorker-2: start
SpawnPoolWorker-1: end
SpawnPoolWorker-3: end
SpawnPoolWorker-2: end
SpawnPoolWorker-1: start
SpawnPoolWorker-3: start
MainProcess: 10
MainProcess: 20
MainProcess: 30
SpawnPoolWorker-1: end
SpawnPoolWorker-3: end
MainProcess: 40
MainProcess: 50
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>apply_async</code>の使い方は、<code>Process</code>と似ていますが、<code>target</code>に相当する部分が<code>func</code>となっています。asyncは非同期を意味するため、以降の&#8221;execute async&#8221;という出力が先にされていることが分かります。なお、関数の返却値は、<code>get</code>メソッドで取得できます。</p>



<p class="wp-block-paragraph">今回プロセスプールのサイズを3としているので、一度に実行されるプロセスは3つまでです。上記結果例では、プロセス1と3が終わり次第、残りの2つプロセスが起動し後続処理が動作していることが分かります。</p>



<h5 class="wp-block-heading jinr-heading d--bold">timeoutの設定</h5>



<p class="wp-block-paragraph"><code>apply_async</code>の実行では、以下のように<span class="marker"><strong><code>timeout</code></strong></span>を指定することができます。変更のない部分は省略しています。<code>timeout</code>は、秒単位で指定し、指定した秒数以内で処理が返ってこない場合は、<code>TimeoutError</code>となります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...(省略)...
 
       # 実行結果を取得
        logging.debug(process1.get(timeout=1))
        logging.debug(process2.get())
        logging.debug(process3.get())
        logging.debug(process4.get())
        logging.debug(process5.get())

...(省略)...</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: execute async
SpawnPoolWorker-2: start
SpawnPoolWorker-3: start
SpawnPoolWorker-1: start
Traceback (most recent call last):
...(省略)...
multiprocessing.context.TimeoutError</pre>



<p class="wp-block-paragraph">上記例では、<code>timeout=1</code>としているので1秒以内に処理が返ってくる必要がありますが、<code>myworker</code>の処理で<code>time.sleep(5)</code>を入れているので処理は1秒以内には終わりません。この場合は、上記のように<code>TimeoutError</code>となります。</p>



<p class="wp-block-paragraph"><code>timeout</code>引数を使うことでタイムアウトを設定しておき、必要に応じて例外処理をするといったことが可能です。</p>



<h4 class="wp-block-heading jinr-heading d--bold">プロセスのブロック apply</h4>



<p class="wp-block-paragraph"><code>apply_async</code>に似たメソッドとして、<span class="marker"><strong><code>apply</code></strong></span>メソッドがあります。このメソッドは、終了するまで後続の処理をブロックします。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import time
from multiprocessing import Pool

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")

# プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker(x: int):
    logging.debug(f"start")
    time.sleep(5)
    logging.debug("end")

    return x


def main():
    logging.debug("start")

    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        # プロセスでブロック
        process = pool.apply(func=myworker, args=(0,))
        logging.debug(process)

        # 上記を実行してから並列実行
        process1 = pool.apply_async(func=myworker, args=(10,))
        process2 = pool.apply_async(func=myworker, args=(20,))
        process3 = pool.apply_async(func=myworker, args=(30,))
        process4 = pool.apply_async(func=myworker, args=(40,))
        process5 = pool.apply_async(func=myworker, args=(50,))

        # 実行結果を取得
        logging.debug(process1.get())
        logging.debug(process2.get())
        logging.debug(process3.get())
        logging.debug(process4.get())
        logging.debug(process5.get())

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
SpawnPoolWorker-1: start
SpawnPoolWorker-1: end
MainProcess: 0
SpawnPoolWorker-2: start
SpawnPoolWorker-3: start
SpawnPoolWorker-1: start
SpawnPoolWorker-1: end
SpawnPoolWorker-3: end
SpawnPoolWorker-2: end
SpawnPoolWorker-1: start
SpawnPoolWorker-3: start
MainProcess: 10
MainProcess: 20
MainProcess: 30
SpawnPoolWorker-1: end
SpawnPoolWorker-3: end
MainProcess: 40
MainProcess: 50
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>apply</code>の使い方は、<code>apply_async</code>と似ていますが、<code>apply</code>は同期的に動作し、指定した関数の終了を待ちます。上記実行結果を見ても分かるように、まず<code>apply</code>で指定した1つのプロセスが実行されて、後続の処理はブロックされます。</p>



<p class="wp-block-paragraph">このことから、並列処理という観点では<code>apply_async</code>の方が適しています。先にある処理を実行してから後続処理を並列で実行するような、処理の順序付けをしたい場合には、<code>apply</code>を使うことでうまく制御できるかと思います。</p>



<h4 class="wp-block-heading jinr-heading d--bold">イテラブルな引数の使用 map</h4>



<p class="wp-block-paragraph"><code>apply_async</code>や<code>apply</code>は一つの引数の組を渡してプロセスを実行しました。例えばリストで保持している引数を順に渡して処理をさせたいような場合には、<code>for</code>文で順次渡すといったことが必要です。しかし、<code>Pool</code>クラスでは、<span class="marker"><strong><code>map</code></strong></span>メソッドが用意されているので、このようなケースでも以下のようにシンプルに記載できます。</p>



<p class="wp-block-paragraph">この<code>map</code>メソッドは、組み込み関数の<code>map</code>の並列版ともいえるものです。ただし、組み込み関数の<code>map</code>は複数のイテラブルを指定できるのに対し、<code>Pool</code>の<code>map</code>メソッドはイテラブルな引数は一つのみサポートするという点で違いがあります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import time
from multiprocessing import Pool

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")

# プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker(x: int):
    logging.debug(f"start")
    time.sleep(5)
    logging.debug("end")

    return x * 2


def main():
    logging.debug("start")

    values = [i for i in range(1, 6)]
    logging.debug(values)

    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        # mapで各プロセスを実行 ↓でブロックされる
        result = pool.map(func=myworker, iterable=values)

        # 全てのプロセスが完了してから表示
        logging.debug("executed")

        # 実行結果を表示
        logging.debug(result)

    logging.debug("end")


if __name__ == "__main__":
    main()
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: [1, 2, 3, 4, 5]
SpawnPoolWorker-1: start
SpawnPoolWorker-3: start
SpawnPoolWorker-2: start
SpawnPoolWorker-1: end
SpawnPoolWorker-1: start
SpawnPoolWorker-2: end
SpawnPoolWorker-3: end
SpawnPoolWorker-2: start
SpawnPoolWorker-1: end
SpawnPoolWorker-2: end
MainProcess: executed
MainProcess: [2, 4, 6, 8, 10]
MainProcess: end</pre>



<p class="wp-block-paragraph">上記の例では、<code>myworker</code>は受け取った値を2倍するようなものになっています。<code>map</code>関数は、<code>apply_async</code>や<code>apply</code>とは異なり、イテラブルな引数を受け取り、その各要素を関数に適用します。<code>map</code>メソッドは、<code>iterable</code>で指定したイテラブルの各要素を順次渡して処理をし、処理結果を返却します。</p>



<p class="wp-block-paragraph"><code>map</code>メソッドでは、<code>map</code>を記載した行で処理がブロックされるため、後続の&#8221;executed&#8221;という文字は全てのプロセスが完了してから表示されます。</p>



<h4 class="wp-block-heading jinr-heading d--bold">イテラブルな引数の使用(非同期版) map_async</h4>



<p class="wp-block-paragraph">上記例で見たように<code>map</code>は後続の処理をブロックするような動作をしました。<code>map</code>の非同期版ともいえるのが、<span class="marker"><strong><code>map_async</code></strong></span>メソッドです。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import time
from multiprocessing import Pool

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")

# プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker(x: int):
    logging.debug(f"start")
    time.sleep(5)
    logging.debug("end")

    return x * 2


def main():
    logging.debug("start")

    values = [i for i in range(1, 6)]
    logging.debug(values)

    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        # map_asyncで非同期に実行
        result = pool.map_async(func=myworker, iterable=values)

        # 非同期なのですぐ表示される
        logging.debug("execute async")

        # 実行結果を表示
        logging.debug(result.get())

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: [1, 2, 3, 4, 5]
MainProcess: execute async
SpawnPoolWorker-1: start
SpawnPoolWorker-2: start
SpawnPoolWorker-3: start
SpawnPoolWorker-1: end
SpawnPoolWorker-2: end
SpawnPoolWorker-3: end
SpawnPoolWorker-1: start
SpawnPoolWorker-2: start
SpawnPoolWorker-2: end
SpawnPoolWorker-1: end
MainProcess: [2, 4, 6, 8, 10]
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>map_async</code>の使い方は、<code>map</code>と同じですが、<code>map_async</code>は非同期であるため&#8221;execute async&#8221;という文字列ががすぐに表示されていることが分かります。</p>



<h5 class="wp-block-heading jinr-heading d--bold">timeoutの設定</h5>



<p class="wp-block-paragraph"><code>map_async</code>の実行では、以下のように<span class="marker"><strong><code>timeout</code></strong></span>を指定することができます。変更のない部分は省略しています。<code>timeout</code>は、秒単位で指定し、指定した秒数以内で処理が返ってこない場合は、<code>TimeoutError</code>となります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...(省略)...

    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        # map_asyncで非同期に実行
        result = pool.map_async(func=myworker, iterable=values)

        # 非同期なのですぐ表示される
        logging.debug("execute async")

        # 実行結果を表示
        logging.debug(result.get(timeout=1))

...(省略)...</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: [1, 2, 3, 4, 5]
MainProcess: execute async
SpawnPoolWorker-1: start
SpawnPoolWorker-2: start
SpawnPoolWorker-3: start
Traceback (most recent call last):
...(省略)...
multiprocessing.context.TimeoutError</pre>



<p class="wp-block-paragraph">上記例では、<code>timeout=1</code>としているので1秒以内に処理が返ってくる必要がありますが、処理で<code>time.sleep(5)</code>を入れているので処理は1秒以内には終わりません。この場合は、上記のように<code>TimeoutError</code>となります。</p>



<p class="wp-block-paragraph"><code>timeout</code>引数を使うことでタイムアウトを設定しておき、必要に応じて例外処理をするといったことが可能です。</p>



<h4 class="wp-block-heading jinr-heading d--bold">イテラブルな引数の使用(遅延評価版) imap</h4>



<p class="wp-block-paragraph"><code>map</code>、<code>map_async</code>と紹介してきましたが、<span class="marker"><strong><code>imap</code></strong></span>という遅延評価版のメソッドもあります。遅延評価とは、必要になるタイミングまで値の評価がされないことを意味します。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import time
from multiprocessing import Pool

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")

# プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker(x: int):
    logging.debug(f"start")
    time.sleep(5)
    logging.debug("end")

    return x * 2


def main():
    logging.debug("start")

    values = [i for i in range(1, 6)]
    logging.debug(values)

    # プロセスプールの生成
    with Pool(PROCESS_POOL_SIZE) as pool:
        # imapでイテレータを生成
        process_iter = pool.imap(func=myworker, iterable=values)
        # ここではイテレータが取得されるだけ
        logging.debug(process_iter)

        logging.debug("execute")

        # 使用するときにイテレータから読みだして実行 (遅延評価)
        for i in process_iter:
            logging.debug(i)

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: [1, 2, 3, 4, 5]
MainProcess: &lt;multiprocessing.pool.IMapIterator object at 0x000001D3AF5D4110>
MainProcess: execute
SpawnPoolWorker-1: start
SpawnPoolWorker-2: start
SpawnPoolWorker-3: start
SpawnPoolWorker-2: end
SpawnPoolWorker-1: end
SpawnPoolWorker-1: start
SpawnPoolWorker-2: start
MainProcess: 2
MainProcess: 4
SpawnPoolWorker-3: end
MainProcess: 6
SpawnPoolWorker-2: end
SpawnPoolWorker-1: end
MainProcess: 8
MainProcess: 10
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>imap</code>は、<code>map</code>や<code>map_async</code>と使用方法は同じですが、返却値がイテレータとなっています。返却値を表示してみていますが「multiprocessing.pool.IMapIterator object」となっていることからもイテレータであることが分かります。</p>



<p class="wp-block-paragraph">その後、&#8221;execute&#8221;という表示をしていますが、このタイミングではプロセスの実行はされていません。具体的にfor文でイテレータから取り出しているタイミングで各プロセスで処理が実行されます。必要なタイミングで処理が動いていることから遅延評価と言います。</p>



<h3 class="wp-block-heading jinr-heading d--bold">プロセス間のデータ交換方法</h3>



<p class="wp-block-paragraph">マルチプロセスでは、複数のプロセスが独立して動作するため、プロセス間のデータ交換の仕組みが必要です。以降では、<code>multiprocessing</code>モジュールでのプロセス間のデータ交換で使用できる<code>Queue</code>と<code>Pipe</code>について紹介します。</p>



<h4 class="wp-block-heading jinr-heading d--bold">Queue</h4>



<p class="wp-block-paragraph">キューを用いたプロセス間のデータ制御をするために<code>multiprocessing</code>モジュールでは、<span class="marker"><strong><code>Queue</code></strong></span>クラスが用意されています。これは<code>queue</code>モジュールの<code>Queue</code>と似ていますが、完全に同じように使用できるわけではありません。実際、<code>multiprocessing.Queue</code>はプロセス間の通信に特化しており、<code>queue.Queue</code>はスレッド間の通信を目的としています。また、例外については<code>queue</code>モジュールの<code>queue.Empty</code>や<code>queue.Full</code>といった例外が送出されます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
import time
from multiprocessing import Process, Queue
from queue import Empty

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")

# プロセスプールのサイズ
PROCESS_POOL_SIZE = 3


def myworker(work_queue):
    logging.debug("start")

    # キューが空でない限り繰り返す
    while not work_queue.empty():
        try:
            item = work_queue.get_nowait()
        except Empty:
            break
        else:
            time.sleep(1)
            logging.debug(item)

    logging.debug("end")


def main():
    logging.debug("start")

    # プロセスで共有するキューを用意する
    work_queue = Queue()

    # 表示したい値のリスト
    vals = [i for i in range(10)]
    # キューに投入する
    for val in vals:
        work_queue.put(val)

    # プロセスプール数のプロセスを用意
    processes = [
        Process(target=myworker, args=(work_queue,))
        for _ in range(PROCESS_POOL_SIZE)
    ]

    # プロセスの開始
    for process in processes:
        process.start()

    # 終了を待機
    for process in processes:
        process.join()

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
Process-1: start
Process-3: start
Process-2: start
Process-2: 2
Process-3: 1
Process-1: 0
Process-2: 3
Process-3: 4
Process-1: 5
Process-1: 8
Process-3: 7
Process-2: 6
Process-3: end
Process-2: end
Process-1: 9
Process-1: end
MainProcess: end</pre>



<p class="wp-block-paragraph">上記例は、<code>Pool</code>クラスでのプロセスプールの例を<code>Queue</code>を使って実装したようなものになっています。処理の構成については、<code>threading</code>におけるでキューの使用を説明している「<a rel="noreferrer noopener" href="https://tech.nkhn37.net/python-threading-multithread/#_queueQueue" data-type="link" data-id="https://tech.nkhn37.net/python-threading-multithread/#_queueQueue" target="_blank">キューを用いたスレッド制御</a>」と同じのため、詳細はそちらを参照してください。</p>



<p class="wp-block-paragraph">ただし、参照先ページの<code>queue.Queue</code>の例では、<code>task_done</code>や<code>join</code>といったメソッドがありますが、<code>multiprocessing</code>の<code>Queue</code>には、<span class="marker"><strong><code>task_done</code>や<code>join</code>といったメソッドがない</strong></span>ので注意してください。</p>



<h4 class="wp-block-heading jinr-heading d--bold">Pipe</h4>



<p class="wp-block-paragraph">各プロセス間でソケットのような双方向の通信チャンネルを作成したい場合には、<span class="marker"><strong><code>Pipe</code></strong></span>を使用します。<code>Pipe</code>は以下のように使用します。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
from multiprocessing import Pipe, Process

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")


class CustomClass:
    pass


def myworker(conn):
    logging.debug("child: start")
    
    # 送信データの用意
    data = [1, "string", {"dict": 0}, CustomClass()]
    logging.debug(f"child: {data}")

    # 親プロセスへデータを送信
    conn.send(data)
    # コネクションをクローズ
    conn.close()

    logging.debug("child: end")


def main():
    logging.debug("parent: start")

    # 親コネクションと子コネクションを生成
    # 引数指定しない場合は、デフォルトはPipe(duplex=True)で双方向になっている
    # Pipe(duplex=False)にするとparrent_conn:受信専用、child_conn:送信専用になる
    parent_conn, child_conn = Pipe()

    child_process = Process(target=myworker, args=(child_conn,))

    # 子プロセスの開始
    child_process.start()

    # 子プロセスからのデータを受信
    data = parent_conn.recv()
    logging.debug(f"parent: {data}")

    # プロセスの終了を待機
    child_process.join()

    logging.debug("parent: end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: parent: start
Process-1: child: start
Process-1: child: [1, 'string', {'dict': 0}, &lt;__mp_main__.CustomClass object at 0x000001A0762E9750>]
Process-1: child: end
MainProcess: parent: [1, 'string', {'dict': 0}, &lt;__main__.CustomClass object at 0x00000225FF32EE90>]
MainProcess: parent: end</pre>



<p class="wp-block-paragraph"><code>Pipe</code>を使用する際には「<code>parent_conn, child_conn = Pipe()</code>」といったように生成します。<code>parent_conn</code>は親コネクションで、<code>child_conn</code>は子コネクションを表しています。</p>



<p class="wp-block-paragraph">子プロセスを生成する際に、<code>child_conn</code>を引数として渡すことで親プロセスとの通信ができるようになります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def myworker(conn):
    logging.debug("child: start")
    
    # 送信データの用意
    data = [1, "string", {"dict": 0}, CustomClass()]
    logging.debug(f"child: {data}")

    # 親プロセスへデータを送信
    conn.send(data)
    # コネクションをクローズ
    conn.close()

    logging.debug("child: end")</pre>



<p class="wp-block-paragraph">子プロセスでは上記のように受け取ったコネクションを使用して、<span class="marker"><strong><code>send</code></strong></span>メソッドで親プロセスにデータを送信できます。コネクションは不要になったら<span class="marker"><strong><code>close</code></strong></span>するようにしましょう。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # 子プロセスからのデータを受信
    data = parent_conn.recv()
    logging.debug(f"parent: {data}")</pre>



<p class="wp-block-paragraph">親プロセス側では、上記の部分で子プロセスからのデータを受信します。データの受信では、親コネクションの<span class="marker"><strong><code>recv</code></strong></span>メソッドを使用します。これにより子プロセスからのデータを受信できます。</p>



<p class="wp-block-paragraph">このように<code>Pipe</code>を使うことで親プロセスと子プロセス間でデータのやり取りができます。なお、<code>Pipe</code>生成時に引数を指定しないデフォルトの場合、デフォルトは<code>Pipe(duplex=True)</code>で双方向通信できるようになっています。そのため、<code>parent_conn</code>の方から<code>send</code>メソッドを使うこともできます。</p>



<p class="wp-block-paragraph">しかし、<code>Pipe(duplex=False)</code>にすると<code>parrent_conn</code>は受信専用、<code>child_conn</code>は送信専用となります。通信方向を制限したい場合には使用を検討するとよいでしょう。もし、<code>duplex=False</code>にした状態で、<code>parent_conn.send</code>を実行しようとすると以下のように例外となります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Traceback (most recent call last):
...(省略)...
OSError: connection is read-only</pre>



<p class="wp-block-paragraph">なお、上記の例ではカスタムクラスのオブジェクトをやり取りするようになっていますが、この場合、オブジェクトがpickle可能であることが必要です。オブジェクトがpickle可能であるというのは、<code>pickle</code>モジュールを使用してシリアル化とデシリアル化が可能であることを意味し、オブジェクトにより可否が分かれるため注意しましょう。</p>



<h3 class="wp-block-heading jinr-heading d--bold">プロセス間におけるデータの共有</h3>



<p class="wp-block-paragraph">マルチプロセス間でデータをやり取りする<code>Queue</code>や<code>Pipe</code>について見てきました。プロセス間でデータをやり取りする方法としてもう一つプロセス間で共有される専用メモリを使う方法があります。以降では共有メモリを使うための仕組みをいくつか紹介します。</p>



<p class="wp-block-paragraph">マルチプロセスでは、プロセス生成時のオーバーヘッドがかかったり、データを交換する手間が発生したりしますが、メモリ空間を共有しないことは誤ってメモリのデータを破壊してしまう危険性が減るため利点と言えます。</p>



<p class="wp-block-paragraph">そのため、基本的にはマルチプロセスでは、以降で紹介するデータ共有の仕組みは使わない方がいいと思います。どうしてもデータを共有しなければならないケースなどでは十分注意して使うようにしましょう。</p>



<h4 class="wp-block-heading jinr-heading d--bold">Value, Array</h4>



<p class="wp-block-paragraph">プロセス間でデータを共有する簡単な方法としては、<span class="marker"><strong><code>Value</code></strong></span>、<span class="marker"><strong><code>Array</code></strong></span>を使う方法があります。これらのオブジェクトは<span class="marker"><strong>共有ctypesオブジェクト（shared ctypes）</strong></span>と言い、プロセス間で共有されるオブジェクトになります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
from multiprocessing import Array, Process, Value

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")


def myworker(num, arr):
    logging.debug("start")
    logging.debug(num)
    logging.debug(arr)

    # 値を変更
    num.value += 1.0
    for i in range(len(arr)):
        arr[i] *= 2

    # 値の表示
    logging.debug(num.value)
    logging.debug(arr[:])

    logging.debug("end")


def main():
    logging.debug("start")

    # "d":double(倍精度浮動小数) "i":sined int(符号付き整数) 他は以下を参照
    # https://docs.python.org/ja/3/library/array.html#module-array
    num = Value("d", 0.0)
    arr = Array("i", range(5))
    # 値の表示
    logging.debug(num.value)
    logging.debug(arr[:])

    # プロセスを生成
    p = Process(target=myworker, args=(num, arr))

    # プロセスの開始
    p.start()

    # プロセスの終了を待機
    p.join()

    # 値の表示
    logging.debug(num.value)
    logging.debug(arr[:])

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: 0.0
MainProcess: [0, 1, 2, 3, 4]
Process-1: start
Process-1: &lt;Synchronized wrapper for c_double(0.0)>
Process-1: &lt;SynchronizedArray wrapper for &lt;multiprocessing.sharedctypes.c_long_Array_5 object at 0x0000017E77500DD0>>
Process-1: 1.0
Process-1: [0, 2, 4, 6, 8]
Process-1: end
MainProcess: 1.0
MainProcess: [0, 2, 4, 6, 8]
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>Value</code>や<code>Array</code>の使い方は簡単で、以下のように生成して使います。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    num = Value("d", 0.0)
    arr = Array("i", range(5))
    # 値の表示
    logging.debug(num.value)
    logging.debug(arr[:])</pre>



<p class="wp-block-paragraph">最初の引数に指定している<code>”d”</code>や<code>"i"</code>は、<code>"d"</code>がdouble(倍精度浮動小数)、<code>"i"</code>がsigned int(符号付き整数)といった型コードを表しています。型コードの指定は<a rel="noreferrer noopener" href="https://docs.python.org/ja/3/library/array.html#module-array" target="_blank">こちら</a>のページを参考にしてください。</p>



<p class="wp-block-paragraph">子プロセス側で変更を加えているのは以下の部分です。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # 値を変更
    num.value += 1.0
    for i in range(len(arr)):
        arr[i] *= 2</pre>



<p class="wp-block-paragraph"><code>Value</code>の値の変更では<code>num.value</code>に対して操作します。また、<code>Array</code>の方は<code>for</code>文で上記のように各要素にアクセスする必要があります。</p>



<p class="wp-block-paragraph">上記結果を見てもわかるように<code>Value</code>や<code>Array</code>は共有されているため、子プロセスで変更を加えた内容を親プロセス側でも直接確認できていることが分かります。</p>



<h4 class="wp-block-heading jinr-heading d--bold">Manager</h4>



<p class="wp-block-paragraph"><code>Value</code>、<code>Array</code>といったデータ共有の仕組みを紹介しましたが、少しPythonらしくなく使いにくい印象を受けたかと思います。</p>



<p class="wp-block-paragraph"><code>multiprocessing</code>モジュールには、共有データを扱いやすくするための<span class="marker"><strong><code>Manager</code></strong></span>というものがあります。<code>Manager</code>はサーバープロセスを管理するためのものとなっています。<code>Manager</code>の使い方を以下の例で見てみましょう。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import logging
from multiprocessing import Manager, Process

logging.basicConfig(level=logging.DEBUG, format="%(processName)s: %(message)s")


def myworker(tmp_l, tmp_d, tmp_n):
    logging.debug("start")
    tmp_l.reverse()
    tmp_d["x"] += 1
    tmp_n.y *= 2

    logging.debug(tmp_l)
    logging.debug(tmp_d)
    logging.debug(tmp_n)

    logging.debug("end")


def main():
    logging.debug("start")

    with Manager() as manager:
        tmp_l = manager.list()
        tmp_d = manager.dict()
        tmp_n = manager.Namespace()

        # 値を設定
        tmp_l.append(1)
        tmp_l.append(2)
        tmp_l.append(3)
        tmp_d["x"] = 0
        tmp_n.y = 1

        # 実行前の値
        logging.debug(tmp_l)
        logging.debug(tmp_d)
        logging.debug(tmp_n)

        p = Process(target=myworker, args=(tmp_l, tmp_d, tmp_n))
        p.start()
        p.join()

        # 実行後の値
        logging.debug(tmp_l)
        logging.debug(tmp_d)
        logging.debug(tmp_n)

    logging.debug("end")


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
MainProcess: start
MainProcess: [1, 2, 3]
MainProcess: {'x': 0}
MainProcess: Namespace(y=1)
Process-2: start
Process-2: [3, 2, 1]
Process-2: {'x': 1}
Process-2: Namespace(y=2)
Process-2: end
MainProcess: [3, 2, 1]
MainProcess: {'x': 1}
MainProcess: Namespace(y=2)
MainProcess: end</pre>



<p class="wp-block-paragraph"><code>Manager</code>は、以下の部分のように<code>with</code>句を使って使用します。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    with Manager() as manager:
        tmp_l = manager.list()
        tmp_d = manager.dict()
        tmp_n = manager.Namespace()

        # 値を設定
        tmp_l.append(1)
        tmp_l.append(2)
        tmp_l.append(3)
        tmp_d["x"] = 0
        tmp_n.y = 1</pre>



<p class="wp-block-paragraph">上記例では、<code>tmp_l</code>がリスト、<code>tmp_d</code>が辞書で通常のリストや辞書のようにデータの操作ができます。<code>tmp_n</code>はNamespaceというもので「.(ドット)」を使ってクラスのプロパティのように値の操作ができます。</p>



<p class="wp-block-paragraph">あとはプロセスの引数としてこれらの変数を渡してあげることで、プロセス間でデータ共有が簡単にできます。</p>



<p class="wp-block-paragraph"><code>Value</code>や<code>Array</code>に比べてPythonのオブジェクトのように扱うことができるため便利です。ただし、注意点として<span class="marker"><strong><code>Value</code>や<code>Array</code>に比べて処理が遅い</strong></span>という特徴がありますので、使いどころをについてはよく検討してもらえると良いかと思います。</p>



<h4 class="wp-block-heading jinr-heading d--bold">異なるサーバー間でのデータ共有</h4>



<p class="wp-block-paragraph"><code>Manager</code>は、ネットワーク越しで異なるサーバー間でデータ共有することも可能です。以下の例で見てみましょう。</p>



<p class="wp-block-paragraph"><strong>server.py</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import queue
from multiprocessing.managers import BaseManager


class QueueManager(BaseManager):
    pass


def main():
    q = queue.Queue()

    # マネージャーで呼び出し可能なオブジェクト登録
    QueueManager.register("get_queue", callable=lambda: q)

    # マネージャーを取得
    # address: マネージャープロセスが新たなコネクションを待ち受けるアドレス
    # authkey: サーバープロセスへのコネクションを検証するための認証キー
    manager = QueueManager(address=("localhost", 50000), authkey=b"P@ssw0rd")

    # サーバーを取得
    server = manager.get_server()
    # サーバーを起動する
    server.serve_forever()


if __name__ == "__main__":
    main()</pre>



<p class="wp-block-paragraph"><strong>client1.py</strong>（データをキューに登録）</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from multiprocessing.managers import BaseManager


class QueueManager(BaseManager):
    pass


def main():
    # オブジェクト登録
    QueueManager.register("get_queue")

    # マネージャーを取得
    manager = QueueManager(address=("localhost", 50000), authkey=b"P@ssw0rd")

    # サーバーへ接続
    manager.connect()
    queue = manager.get_queue()
    queue.put("hello")


if __name__ == "__main__":
    main()</pre>



<p class="wp-block-paragraph"><strong>client2.py</strong>（データをキューから取り出す）</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from multiprocessing.managers import BaseManager


class QueueManager(BaseManager):
    pass


def main():
    # オブジェクト登録
    QueueManager.register("get_queue")

    # マネージャーを取得
    manager = QueueManager(address=("localhost", 50000), authkey=b"P@ssw0rd")

    # サーバーへ接続
    manager.connect()
    queue = manager.get_queue()
    print(queue.get())


if __name__ == "__main__":
    main()</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】()内は手順の補足
(1. サーバーの立ち上げ)
> python server.py

(2. 別のターミナルを立ち上げて実行: "hello"がキューにputされる)
> python client1.py

(3. 別のターミナルを立ち上げて実行: "hello"がキューからgetされて表示される。キューに値がない場合は待ち状態になり、client1でputされたら表示される)
> python .\multi_client2.py 
hello</pre>



<p class="wp-block-paragraph">上記例では、3つのプログラムを作っています。server.pyがサーバーで動作し、client1.pyがデータをキューに登録し、client2.pyがキューからデータを取り出すクライアントとして動いているようなイメージを持ってもらえればと思います。</p>



<h5 class="wp-block-heading jinr-heading d--bold">server.pyの内容</h5>



<p class="wp-block-paragraph">まずは、server.pyの内容から見ていきましょう。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from multiprocessing.managers import BaseManager


class QueueManager(BaseManager):
    pass</pre>



<p class="wp-block-paragraph"><code>multiprocessing.managers</code>から<code>BaseManager</code>をインポートし、<code>BaseManager</code>を継承した<code>QueueManager</code>というクラスを作成しています。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def main():
    q = queue.Queue()

    # マネージャーで呼び出し可能なオブジェクト登録
    QueueManager.register("get_queue", callable=lambda: q)

    # マネージャーを取得
    # address: マネージャープロセスが新たなコネクションを待ち受けるアドレス
    # authkey: サーバープロセスへのコネクションを検証するための認証キー
    manager = QueueManager(address=("localhost", 50000), authkey=b"P@ssw0rd")

    # サーバーを取得
    server = manager.get_server()
    # サーバーを起動する
    server.serve_forever()</pre>



<p class="wp-block-paragraph">メイン関数では、<code>queue</code>モジュールから<code>Queue</code>を生成し、<code>register</code>メソッドで、呼び出し可能なものとして登録をしています。これにより、<code>manager.get_queue()</code>でキューを取り出すことができます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    manager = QueueManager(address=("localhost", 50000), authkey=b"P@ssw0rd")</pre>



<p class="wp-block-paragraph"><code>QueueManager</code>の生成時には、<code>address</code>を使ってコネクションを待ち受けるアドレスとポートを指定し、<code>authkey</code>で認証キーを設定します。これらの値をclient側で用いることでアクセスできるようになります。今回は、自端末で確認できるようにlocalhostとしていますが、実際にはサーバーのアドレスを入力します。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # サーバーを取得
    server = manager.get_server()
    # サーバーを起動する
    server.serve_forever()</pre>



<p class="wp-block-paragraph">あとは、サーバーを取得、起動します。<code>serve_forever</code>を実行することでサーバー機能が常時動く状態となります。</p>



<h5 class="wp-block-heading jinr-heading d--bold">client1.pyの内容</h5>



<p class="wp-block-paragraph">次にデータをキューに登録するclient1.pyの処理を見てみます。作りはserver.pyに非常に似ており、クラス定義やオブジェクト登録、マネージャーの取得の部分は同じです。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # サーバーへ接続
    manager.connect()
    queue = manager.get_queue()
    queue.put("hello")</pre>



<p class="wp-block-paragraph">サーバーへ接続しているところが上記の部分ですが、<code>connect</code>を使うことでサーバーに接続します。<code>get_queue</code>によりキューを取得できるので、キューに&#8221;hello&#8221;という文字列を<code>put</code>で登録しています。</p>



<h5 class="wp-block-heading jinr-heading d--bold">client2.pyの内容</h5>



<p class="wp-block-paragraph">最後に、キューから値を取り出すclient2.pyの処理ですが、client1.pyとほとんど同じです。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    # サーバーへ接続
    manager.connect()
    queue = manager.get_queue()
    print(queue.get())</pre>



<p class="wp-block-paragraph">キューの操作では、<code>put</code>ではなく<code>get</code>で値を取得している点がclient1と異なります。</p>



<h5 class="wp-block-heading jinr-heading d--bold">処理の実行手順</h5>



<p class="wp-block-paragraph">動作の確認の手順についてです。まずは、server1.pyを実行します。<code>serve_forever</code>でサーバーが起動状態となります。</p>



<p class="wp-block-paragraph">次に別のプロンプトを開いてclient1.pyを実行します。これによりキューに&#8221;hello&#8221;という文字列が登録されます。</p>



<p class="wp-block-paragraph">最後にまた別のターミナルを開いてclient2.pyを実行します。すると、client1.pyがキューに登録した&#8221;hello&#8221;という文字列が表示されます。</p>



<p class="wp-block-paragraph">複数回client2.pyを実行すると待ち状態になります。これは、キューに値が入らないので待っている状態です。その状態で、client1.pyを実行すると、client2.py側ですぐに&#8221;hello&#8221;と表示されます。</p>



<p class="wp-block-paragraph">今回の実行手順は自端末(localhost)での確認方法で紹介しましたが、それぞれサーバー、クライアントで実行することで異なるサーバー間でもデータ共有が可能です。</p>



<h2 class="wp-block-heading jinr-heading d--bold">まとめ</h2>



<p class="wp-block-paragraph">Pythonで<span class="marker"><strong>マルチプロセスを実装するための<code>multiprocessing</code>モジュールの使い方</strong></span>について解説しました。</p>



<p class="wp-block-paragraph">並列処理を実装する場合には、マルチスレッドとマルチプロセスという選択肢が考えられます。Pythonにおいてはグローバルインタープリタロック(GIL)の影響でマルチスレッドではCPUバウンドな処理では十分な効果を発揮できない可能性があります。</p>



<p class="wp-block-paragraph">マルチプロセスの場合、各プロセスがGILの影響を受けない独自の実行環境を持つため、リソースをより効果的に活用できます。Pythonを使用してマルチコアCPU上でのCPU負荷が高いタスクを実行する場合、マルチプロセスの利用は非常に妥当であると言えます。</p>



<p class="wp-block-paragraph"><code>multiprocessing</code>モジュールでは、マルチプロセスを実行するためにプロセスプール(<code>Pool</code>)やデータ交換方法(<code>Queue</code>、<code>Pipe</code>)・データ共有方法(<code>Value</code>、<code>Array</code>、<code>Manager</code>)といった便利な機能を提供してくれます。</p>



<p class="wp-block-paragraph">並列処理は難しいですが、ぜひ<code>multiprocessing</code>モジュールの使い方を覚えてもらえるといいかと思います。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--simple-iconbox6 ">
			<i class="jif jin-ifont-v2books" aria-hidden="true"></i>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph"><code>multiprocessing</code> の公式ドキュメントは<a href="https://docs.python.org/ja/3/library/multiprocessing.html" target="_blank" rel="noreferrer noopener">こちら</a>を参照してください。</p>
</div>
		</div></section>



<section class="wp-block-jinr-blocks-simplebox b--jinr-block-container"><div class="b--jinr-block b--jinr-box d--heading-box8  "><div class="a--simple-box-title d--bold">ソースコード</div><div class="c--simple-box-inner">
<p class="wp-block-paragraph">上記で紹介しているソースコードについては <a href="https://github.com/nkhn37/python-tech-sample-source/tree/main/python-libraries/multiprocessing" target="_blank" rel="noreferrer noopener">GitHub</a> にて公開しています。参考にしていただければと思います。</p>
</div></div></section>


<section class="b--jinr-block b--jinr-blogcard d--blogcard-hover-up d--blogcard-style1 d--blogcard-mysite t--round "><div class="a--blogcard-label ef">あわせて読みたい</div><a class="o--blogcard-link t--round" href="https://tech.nkhn37.net/python-tech-summary-page/"><div class="c--blogcard-image"><img decoding="async" class="a--blogcard-img-src" width="128" height="72" src="https://tech.nkhn37.net/wp-content/uploads/2024/08/Python-Tech-Pythonプログラミングガイド_new1-640x360.jpg" alt="【Python Tech】プログラミングガイド" /></div><div class="a--blogcard-title d--bold">【Python Tech】プログラミングガイド</div></a></section>]]></content:encoded>
					
					<wfw:commentRss>https://tech.nkhn37.net/python-multiprocessing-basics/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【Python】map関数の使い方の基本 ~要素への関数適用~</title>
		<link>https://tech.nkhn37.net/python-map-basic/</link>
					<comments>https://tech.nkhn37.net/python-map-basic/#respond</comments>
		
		<dc:creator><![CDATA[naoki-hn]]></dc:creator>
		<pubDate>Wed, 23 Aug 2023 20:00:00 +0000</pubDate>
				<category><![CDATA[関数]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[高階関数]]></category>
		<guid isPermaLink="false">https://tech.nkhn37.net/?p=8806</guid>

					<description><![CDATA[Python の組み込み関数の map 関数を使って要素へ関数を適用する方法を解説します。 map 関数で要素へ関数を適用する map 関数は、関数と処理対象のイテラブルを受け取って、イテラブルの各要素に関数を適用した結 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Python の組み込み関数の <span class="marker"><strong><code>map</code> 関数を使って要素へ関数を適用する方法</strong></span>を解説します。</p>



<h2 class="wp-block-heading jinr-heading d--bold"><code>map</code> 関数で要素へ関数を適用する</h2>



<p class="wp-block-paragraph"><span class="marker"><strong><code>map</code></strong></span> 関数は、関数と処理対象のイテラブルを受け取って、イテラブルの各要素に関数を適用した結果を返す Python の組み込み関数です。</p>



<p class="wp-block-paragraph">このように引数に関数を受け取る関数は<span class="marker"><strong>高階関数</strong></span>と言います。<code>for</code> 文で処理しても同じ処理ができますが、繰り返し処理を書かずにシンプルに記載できる点がメリットです。</p>



<p class="wp-block-paragraph"><code>map</code> 関数は、関数型プログラミング言語において中心的な関数です。Python はマルチパラダイムの言語と言われているため、厳密ではありませんが関数型のプログラミングスタイルもサポートしており、<code>map</code> 関数が組み込み関数として利用できます。</p>



<p class="wp-block-paragraph">本記事では、リストの要素へ関数を適用する例を用いて、<span class="marker"><strong><code>map</code> 関数の使い方の基本</strong></span>を紹介します。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--simple-iconbox6 ">
			<i class="jif jin-ifont-v2books" aria-hidden="true"></i>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph"><code>map</code> 関数と同様に関数型プログラミングに関連のある関数として <code>filter</code> 関数、<code>reduce</code> 関数、<code>partial</code> 関数があります。以下ページも参考にしてください。</p>



<ul class="wp-block-list jinr-list">
<li><a href="https://tech.nkhn37.net/python-filter-basic/" target="_blank" rel="noreferrer noopener">filter関数の使い方の基本 ~条件を満たす要素を抽出~</a></li>



<li><a href="https://tech.nkhn37.net/python-reduce-basic/" target="_blank" rel="noreferrer noopener">reduce関数の使い方の基本 ~要素の畳み込み~</a></li>



<li><a href="https://tech.nkhn37.net/python-partial-basic/" target="_blank" rel="noreferrer noopener">partial関数の使い方の基本 ~関数の部分適用~</a></li>
</ul>
</div>
		</div></section>



<h3 class="wp-block-heading jinr-heading d--bold"><code>map</code> 関数の基本的な使い方</h3>



<p class="wp-block-paragraph"><span class="marker"><strong><code>map</code></strong></span> 関数の基本的な構文は以下のようになります。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">result = map(関数, イテラブル)</pre>



<p class="wp-block-paragraph">複数の引数を取る関数を指定する場合には、以下のようにイテラブルを列挙して指定することができます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">result = map(関数, イテラブル1, イテラブル2, ...)</pre>



<p class="wp-block-paragraph"><code>map</code> 関数では、イテラブルの各要素に関数を適用した結果のイテレータを返却します。イテレータは値を逐次取り出せるオブジェクトで、<code>next</code>を用いることで順に関数を適用した値を取り出したり、<code>for</code> 文で使用したりすることができます。後述の例でも見ますが、<code>list(map(関数, イテラブル))</code> のようにすることでリストに変換して取得することも可能です。</p>



<p class="wp-block-paragraph">また、<code>map(関数, イテラブル1, イテラブル2, ...)</code>というように複数のイテラブルを渡す場合には、関数は引数としてイテラブルの数だけ受け取れる必要があります。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--simple-iconbox6 ">
			<i class="jif jin-ifont-v2books" aria-hidden="true"></i>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph">イテレータの基本は以下を参考にしてください。</p>



<p class="wp-block-paragraph"><a href="https://tech.nkhn37.net/python-iterator-basics/" target="_blank" rel="noreferrer noopener">イテレータ（iterator）の基本</a></p>
</div>
		</div></section>



<h4 class="wp-block-heading jinr-heading d--bold">基本的な使い方</h4>



<p class="wp-block-paragraph"><code>map</code> 関数の基本的な使い方を以下の例を用いて見ていきましょう。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def square(x):
    """値を2乗する"""
    return x**2


if __name__ == "__main__":
    tmp_list = [1, 2, 3, 4, 5]

    # map関数を適用し、イテレータを取得
    result = map(square, tmp_list)
    print(result, "\n")

    # nextで1要素ずつ結果データを取得
    print(next(result))
    print(next(result))
    print(next(result))
    print(next(result))
    print(next(result))</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
&lt;map object at 0x0000018E82BC2A70> 

1
4
9
16
25</pre>



<p class="wp-block-paragraph">上記の例では、受け取った値を 2 乗する <code>square</code> という関数が定義されており、<code>tmp_list</code> の各要素に適用しています。</p>



<p class="wp-block-paragraph"><code>map</code> 関数を呼び出している部分では「<code>map(square, tmp_list)</code>」というように関数と処理対象のリストを渡しています。ここで<span class="marker"><strong>ポイントは、関数を渡す際に <code>()</code> がない</strong></span>ことです。関数は <code>square(2)</code> のように <code>()</code> で引数を指定すると関数を実行することを意味しますが、<code>()</code> がない場合は関数そのものを表します。<code>map</code> 関数は、受け取った <code>square</code> という関数を内部で実行しているわけです。</p>



<p class="wp-block-paragraph"><code>map</code> 関数の返却値は「<code>map object</code>」となっていますが、これはイテレータの一種です。<code>next</code> 関数を使用することで次の要素を順番に取得することができます。この例では、<code>tmp_list</code> の要素それぞれを2乗した結果が順に取得されていることが分かります。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--heading-iconbox1 ">
			<div class="a--heading-iconbox-title">
			<div class="a--iconbox-title-icon"><i class="jif jin-ifont-caution" aria-hidden="true"></i></div>
			<div class="a--iconbox-title-text">注意点</div>
			</div>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph">上記例で <code>next(result)</code> を追加して再度実行すると <code>StopIteration</code> 例外が発生します。これはイテレータがもう要素を持たない場合に発生する例外です。<code>StopIteration</code> は <code>for</code> ループ等の繰り返しの終了を判断するために使用されています。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
Traceback (most recent call last):
...(省略)...
StopIteration</pre>
</div>
		</div></section>



<h4 class="wp-block-heading jinr-heading d--bold">リストやタプルで取得する場合</h4>



<p class="wp-block-paragraph"><code>map</code> 関数の返却値はイテレータであるため、以下のように <code>list</code> や <code>tuple</code> に変換することができます。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def square(x):
    """値を2乗する"""
    return x**2


if __name__ == "__main__":
    tmp_list = [1, 2, 3, 4, 5]

    # map関数を適用し、リストで取得
    result_list = list(map(square, tmp_list))
    print(result_list)

    # map関数を適用し、タプルで取得
    result_tuple = tuple(map(square, tmp_list))
    print(result_tuple)</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
[1, 4, 9, 16, 25]
(1, 4, 9, 16, 25)</pre>



<h4 class="wp-block-heading jinr-heading d--bold">複数イテラブルを指定する場合</h4>



<p class="wp-block-paragraph"><code>map</code> 関数は複数のイテラブルを受け取り、それらに対してまとめて関数を適用することができます。以下の例では、<code>make_variable_set</code> という関数が 3 つの引数を受け取り、それらをタプルとして返す動作をします。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def make_variable_set(x, y, z):
    """各引数を並べたタプルを返却する"""
    return x, y, z


if __name__ == "__main__":
    nums1 = [1, 2, 3]
    nums2 = [4, 5, 6]
    nums3 = [7, 8, 9]

    # map関数で複数のイテラブルを渡す
    # 関数側もイテラブルの数だけの引数を受け取れる必要がある
    result = list(map(make_variable_set, nums1, nums2, nums3))
    print(result)</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]</pre>



<p class="wp-block-paragraph">この結果を見ると、3 つのリストの各要素が関数によってタプルとしてまとめられて返されていることが確認できます。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--heading-iconbox1 ">
			<div class="a--heading-iconbox-title">
			<div class="a--iconbox-title-icon"><i class="jif jin-ifont-caution" aria-hidden="true"></i></div>
			<div class="a--iconbox-title-text">注意点</div>
			</div>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph"><code>map(関数, イテラブル1, イテラブル2, ...)</code> というように複数のイテラブルを渡す場合、関数は指定するイテラブルの数だけ引数を受け取ることができるように設計されている必要があります。</p>
</div>
		</div></section>



<h4 class="wp-block-heading jinr-heading d--bold">複数のイテラブルの要素数が異なる場合</h4>



<p class="wp-block-paragraph"><code>map</code> 関数を使用する際、複数のイテラブルを指定することができますが、それらの要素数が異なる場合の挙動に注意が必要です。</p>



<p class="wp-block-paragraph">具体的には、<code>map</code> 関数は、<span class="marker"><strong>最も短いイテラブルの要素数に合わせて</strong></span>処理を行います。これは最も短いイテラブルが終了した時点で、他のイテラブルの残りの要素は無視されることを意味します。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def make_variable_set(x, y, z):
    """各引数を並べたタプルを返却する"""
    return x, y, z


if __name__ == "__main__":
    nums1 = [1, 2, 3]
    nums2 = [4, 5, 6, 7, 8, 9]
    nums3 = [10, 11, 12, 13, 14]

    # map関数で複数のイテラブルを渡す
    # 最も短いイテラブルが消費されたら終了
    result = list(map(make_variable_set, nums1, nums2, nums3))
    print(result)</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
[(1, 4, 10), (2, 5, 11), (3, 6, 12)]</pre>



<p class="wp-block-paragraph">この例では、3 つの要素を使用していますが、最も短いイテラブルである <code>nums1</code> に合わせて処理が行われるため、<code>nums2</code> の <code>7</code>、<code>8</code>、<code>9</code> や <code>nums3</code> の <code>13</code>、<code>14</code> については結果に含まれていないことが分かります。</p>



<h4 class="wp-block-heading jinr-heading d--bold">ラムダ関数（無名関数）と組み合わせる場合</h4>



<p class="wp-block-paragraph"><code>map</code> 関数は、<span class="marker"><strong>ラムダ関数（無名関数）</strong></span>と組み合わせることで、より手軽に要素に対して関数を適用することができます。ラムダ関数は、関数定義を <code>def</code> で定義することなく、その場で作れるもので関数型プログラミングでは中核の概念です。</p>



<p class="wp-block-paragraph"><code>square</code> 関数をラムダ関数にして <code>map</code> 関数で使う例を見てみましょう。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">tmp_list = [1, 2, 3, 4, 5]

# map関数を適用し、リストで取得
result = list(map(lambda x: x**2, tmp_list))
print(result)

# map関数を適用し、タプルで取得
result = tuple(map(lambda x: x**2, tmp_list))
print(result)</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">【実行結果】
[1, 4, 9, 16, 25]
(1, 4, 9, 16, 25)</pre>



<p class="wp-block-paragraph">これまでの例で <code>square</code> と関数を指定していた部分に「<code>lambda x: x**2</code>」というラムダ関数を指定しています。結果は <code>square</code> 関数を使った場合と同じす。</p>



<p class="wp-block-paragraph">ラムダ関数は無名関数ともいわれる通り、関数名はありませんが、そのものが関数として扱われるため、<code>map</code> 関数に渡すことでリストの各要素に関数を適用できます。</p>



<section class="wp-block-jinr-blocks-iconbox b--jinr-block b--jinr-iconbox"><div class="d--simple-iconbox6 ">
			<i class="jif jin-ifont-v2books" aria-hidden="true"></i>
			<div class="a--jinr-iconbox">
<p class="wp-block-paragraph">ラムダ関数の基本については以下を参考にしてください。</p>



<p class="wp-block-paragraph"><a href="https://tech.nkhn37.net/python-lambda/" target="_blank" rel="noreferrer noopener">ラムダ（lambda）関数：無名関数の使い方</a></p>
</div>
		</div></section>



<h2 class="wp-block-heading jinr-heading d--bold">まとめ</h2>



<p class="wp-block-paragraph">Python の組み込み関数である <span class="marker"><strong><code>map</code> 関数を使って要素へ関数を適用する方法</strong></span>を解説しました。</p>



<p class="wp-block-paragraph"><code>map</code> 関数は、関数と処理対象のイテラブルを受け取って、イテラブルの各要素に関数を適用した結果を返す Python の組み込み関数です。<code>map</code> 関数は他の関数型プログラミング言語でも中心的な関数で Python でも使用できます。</p>



<p class="wp-block-paragraph"><code>map</code> 関数は <code>for</code> 文を用いなくてもシンプルに関数の適用ができることが特徴です。非常に強力な機能ではありますが、<code>map</code> 関数の適用が常に最適な選択というわけではありません。</p>



<p class="wp-block-paragraph">場合によっては <code>for</code> 文で書いた方が可読性が上がる可能性もあります。目的に応じて適切に方法を選択できるよう <code>map</code> 関数の使い方を理解しておくようにしましょう。</p>



<section class="wp-block-jinr-blocks-simplebox b--jinr-block-container"><div class="b--jinr-block b--jinr-box d--heading-box8  "><div class="a--simple-box-title d--bold">ソースコード</div><div class="c--simple-box-inner">
<p class="wp-block-paragraph">上記で紹介しているソースコードについては <a href="https://github.com/nkhn37/python-tech-sample-source/tree/main/python-basic/map" target="_blank" rel="noreferrer noopener">GitHub</a> にて公開しています。参考にしていただければと思います。</p>
</div></div></section>


<section class="b--jinr-block b--jinr-blogcard d--blogcard-hover-up d--blogcard-style1 d--blogcard-mysite t--round "><div class="a--blogcard-label ef">あわせて読みたい</div><a class="o--blogcard-link t--round" href="https://tech.nkhn37.net/python-tech-summary-page/"><div class="c--blogcard-image"><img decoding="async" class="a--blogcard-img-src" width="128" height="72" src="https://tech.nkhn37.net/wp-content/uploads/2024/08/Python-Tech-Pythonプログラミングガイド_new1-640x360.jpg" alt="【Python Tech】プログラミングガイド" /></div><div class="a--blogcard-title d--bold">【Python Tech】プログラミングガイド</div></a></section>]]></content:encoded>
					
					<wfw:commentRss>https://tech.nkhn37.net/python-map-basic/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Disk: Enhanced  を使用したページ キャッシュ

Served from: tech.nkhn37.net @ 2026-06-14 07:07:54 by W3 Total Cache
-->