C#からPythonスクリプト(.py)を実行する3つの方法を徹底比較

C#を用いた開発プロジェクトにおいて、Pythonが持つ豊富なライブラリ(例えば、機械学習のTensorFlowやPyTorch、データ分析のNumPyやPandasなど)を活用したい場面が出てくることがあります。その際、C#のプログラムから直接Pythonスクリプト(.pyファイル)を実行できれば、開発の幅が大きく広がります。

C#からPythonスクリプトを実行するには、いくつかの異なるアプローチが存在します。この記事では、代表的な3つの方法について、それぞれの特徴、メリット・デメリット、そして具体的なサンプルコードを交えて詳しく解説します。

目次

C#からPythonスクリプトを実行する3つの主な方法

主な方法は以下の3つです。それぞれに特性があるため、実現したいことや環境に応じて最適なものを選択する必要があります。

  1. Process.Start (プロセスとして実行)
    • C#からpython.exeという別のプログラムを起動し、スクリプトを実行させる最もシンプルな方法です。
  2. IronPython
    • Pythonを.NET Framework上で動作させるための実装です。C#とPython間のデータ連携がスムーズに行えます。
  3. Python for .NET (pythonnet)
    • PCにインストールされているCPython(通常のPython)と.NETを強力に統合するライブラリです。

それぞれの詳細を見ていきましょう。

方法1:プロセスとして実行 (Process.Start)

これは、C#からPythonの実行ファイル(python.exe)をコマンドラインから叩くのと同じ動作をさせる方法です。C#の標準機能であるSystem.Diagnostics.Processクラスを使用します。

メリット

  • 手軽さ: C#の標準機能のみで実現でき、追加のライブラリ(NuGetパッケージ)が不要です。
  • 環境非依存: PCにインストールされている通常のPython環境で動作するため、NumPyやPandasなど、C拡張モジュールを含む全てのPythonライブラリがそのまま利用できます。

デメリット

  • データ連携の制約: C#とPython間でのデータのやり取りは、「標準出力(print文など)」や「ファイル」を介して行う必要があります。複雑なオブジェクト(リストや辞書など)を渡すには、JSON形式の文字列に変換するなど、一手間かかります。
  • 実行コスト: 実行のたびに新しいプロセスが起動するため、頻繁に呼び出すとオーバーヘッドが大きくなる可能性があります。

サンプルコード

実行するPythonスクリプトと、それを呼び出すC#コードの例です。

Python側 (my_script.py)

C#から渡された引数をsys.argvで受け取り、結果をprint文で(標準出力へ)返します。

# my_script.py
import sys

# C#から渡された引数を取得 (インデックス0はスクリプト名)
arg1 = sys.argv[1]
arg2 = sys.argv[2]

# 簡単な処理
result = f"Python Script: Received '{arg1}' and '{arg2}'. Hello from Python!"

# C#側で受け取るために結果を標準出力へ
print(result)

C#側 (ProcessRunner.cs)

using System;
using System.Diagnostics;
using System.IO;

public class ProcessRunner
{
    public static string RunPythonScript(string scriptPath, string arg1, string arg2)
    {
        string result = "";
        
        try
        {
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                FileName = "python", // python.exeへのパス (PATHが通っている前提)
                Arguments = $"\"{scriptPath}\" \"{arg1}\" \"{arg2}\"",
                UseShellExecute = false,    // シェルを経由しない
                CreateNoWindow = true,      // コンソールウィンドウを表示しない
                RedirectStandardOutput = true, // 標準出力をC#で受け取る
                RedirectStandardError = true   // エラー出力をC#で受け取る
            };

            using (Process process = Process.Start(startInfo))
            {
                // 標準出力を同期的に読み取る
                result = process.StandardOutput.ReadToEnd();
                string error = process.StandardError.ReadToEnd();
                
                process.WaitForExit();

                if (!string.IsNullOrEmpty(error))
                {
                    // エラー処理
                    Console.WriteLine("Python Error: " + error);
                    return "Error: " + error;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("An error occurred: " + ex.Message);
            // Pythonがインストールされていない、PATHが通っていない等の可能性
            return "Exception: " + ex.Message;
        }

        return result;
    }

    // 実行例
    public static void Main(string[] args)
    {
        string pyScript = @"C:\path\to\your\my_script.py"; // スクリプトのパス
        string output = RunPythonScript(pyScript, "CSharpValue1", "CSharpValue2");
        
        Console.WriteLine("--- Result from Python ---");
        Console.WriteLine(output);
    }
}

方法2:IronPython を利用する

IronPythonは、Pythonの.NET実装です。C#と同じ.NET環境(CLR)上で動作するため、C#とPythonのコードを非常にシームレスに連携させることができます。

メリット

  • スムーズなデータ連携: C#のオブジェクトをPythonに直接渡したり、Pythonの変数をC#で直接受け取ったりできます。プロセス起動のオーバーヘッドもありません。

デメリット

  • ライブラリの制約: 最大の注意点として、IronPythonはCPython(通常のPython)ではありません。そのため、NumPyやPandasなど、C言語で実装された拡張モジュールに依存するライブラリは利用できません。
  • 開発の停滞: 最新のPythonバージョンへの追従が遅れがちになる傾向があります。

サンプルコード

まず、NuGetパッケージマネージャで IronPython をインストールする必要があります。

Python側 (my_script_iron.py)

C#側で設定された変数をそのまま利用できます。

# my_script_iron.py

# 'csharp_variable' はC#側で scope.SetVariable を使って設定される
output_variable = f"IronPython received '{csharp_variable}' and processed it."

C#側 (IronPythonRunner.cs)

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using System;

public class IronPythonRunner
{
    public static void Main(string[] args)
    {
        // Pythonエンジンとスコープ(変数の格納場所)を作成
        ScriptEngine engine = Python.CreateEngine();
        ScriptScope scope = engine.CreateScope();

        // C#からPythonへ渡す変数を設定
        string myVar = "Hello from C#";
        scope.SetVariable("csharp_variable", myVar);

        // Pythonスクリプトファイルを実行
        string scriptPath = @"C:\path\to\your\my_script_iron.py"; 
        engine.ExecuteFile(scriptPath, scope);

        // Python側で設定された変数をC#で受け取る
        string result = scope.GetVariable<string>("output_variable");

        Console.WriteLine("--- Result from IronPython ---");
        Console.WriteLine(result);
    }
}

方法3:Python for .NET (pythonnet) を利用する

pythonnetは、C#(.NET)とCPython(PCにインストールされている通常のPython)を緊密に統合するためのライブラリです。「方法1」の手軽さと「方法2」の密なデータ連携の、両方の利点を併せ持つ強力な選択肢です。

メリット

  • 完全なPython環境の利用: CPythonで動作するため、NumPy, Pandas, Scikit-learn, TensorFlowなど、C拡張モジュールを含む全てのPythonライブラリが利用可能です。
  • スムーズなデータ連携: IronPythonと同様に、.NETとPython間でのデータ(オブジェクト)の相互変換がスムーズに行えます。

デメリット

  • 環境設定: Pythonがインストールされているパスを正しく認識させるなど、初期の環境設定が他の方法に比べて少し複雑になる場合があります。

サンプルコード

まず、NuGetパッケージマネージャで pythonnet をインストールします。

Python側 (my_script_pynet.py)

NumPyを使った関数の例です。

# my_script_pynet.py
import numpy as np

def process_data(csharp_list):
    # C#から受け取ったリストをNumPy配列に変換
    # (pythonnetが自動的に型変換を試みる)
    arr = np.array(csharp_list)
    
    # NumPyの関数(平均値)を使って計算
    mean_val = np.mean(arr)
    
    return f"The mean of {csharp_list} is {mean_val}"

C#側 (PythonNetRunner.cs)

using Python.Runtime;
using System;

public class PythonNetRunner
{
    public static void Main(string[] args)
    {
        // Python環境の初期化
        // (環境変数PYTHONNET_PYDLLなどでPythonのDLLパスを指定することが推奨される)
        PythonEngine.Initialize();

        // PythonのGIL (Global Interpreter Lock) を取得
        using (Py.GIL())
        {
            try
            {
                // Pythonスクリプトをモジュールとしてインポート
                // (my_script_pynet.py がPythonの検索パス上にある必要がある)
                dynamic myModule = Py.Import("my_script_pynet");

                // C#のデータを準備
                var data = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0 };

                // Pythonの関数を呼び出し、C#のデータを渡す
                var result = myModule.process_data(data);

                // 結果をC#の文字列に変換して表示
                Console.WriteLine("--- Result from pythonnet ---");
                Console.WriteLine(result.ToString());
            }
            catch (PythonException ex)
            {
                Console.WriteLine("Python Error: " + ex.Message);
            }
        }

        // Python環境をシャットダウン
        PythonEngine.Shutdown();
    }
}

(補足: pythonnetのセットアップでは、実行環境(32bit/64bit)やPythonのバージョンをC#プロジェクト側と一致させる必要があります。)

どの方法を選ぶべきか?(比較まとめ)

3つの方法を比較します。

方法仕組み長所短所主な用途
Process.Start外部プログラム(python.exe)として起動手軽全てのPythonライブラリが使えるデータ連携が文字列などに限られる簡単なスクリプトの実行、バッチ処理
IronPython.NET上でPythonを直接実行データ連携が非常にスムーズNumPyなどC拡張ライブラリが使えない.NET中心で、Pythonの基本機能だけ使いたい場合
pythonnetCPythonを.NETプロセスに組み込むデータ連携がスムーズ かつ 全てのPythonライブラリが使える環境設定がやや複雑になる可能性機械学習など、高度なPythonライブラリをC#から利用したい場合(推奨)

結論として、以下のように選択するのが良いでしょう。

  • 単純にPythonスクリプトを実行したいだけ(データ連携が不要、または単純な文字列で良い):方法1 (Process.Start) が最も簡単です。
  • C#とPythonで密なデータ連携が必要だが、NumPyなどの高度なライブラリは不要:方法2 (IronPython) が選択肢になります。
  • NumPy, Pandas, TensorFlowなど、Pythonの強力なライブラリをC#から活用したい:方法3 (pythonnet) が最も強力でモダンな選択肢です。

まとめ

C#からPythonスクリプトを実行する3つの方法を紹介しました。それぞれに一長一短がありますが、特に「pythonnet」の登場により、C#とPython(CPython)の強力な連携が現実的なものとなっています。

C#の堅牢なシステム構築能力と、Pythonの柔軟で強力なライブラリ群を組み合わせることで、開発できるアプリケーションの可能性が大きく広がります。プロジェクトの要件に合わせて、最適な連携方法を選択してください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次