C#/.net プログラムが Python を呼び出す
C# の利点は、強力なだけでなく、開発サイクルが短い、ウィンドウ内での開発にあります。一方、Python にはサードパーティのライブラリが豊富にあり、ホイールを自前で作成することを避け、C# を使用してインターフェイスを作成でき、Python を使用した具体的な実装により開発効率が大幅に向上します。pythonnet
この記事ではPythonスクリプトを実行するための使い方を紹介しますpythonnet
。これを利用すると高い対話性が得られるだけでなく、サードパーティ製のPythonライブラリを利用することができます。同時にプログラムに必要なPython環境とサードパーティ製ライブラリをパッケージ化することができます。ユーザーが Python 環境を操作できないようにするためにソフトウェアに組み込まれます。
C# で Python を呼び出す一般的なメソッド
Pythonを呼び出す一般的な方法は4つあります
道 | アドバンテージ | 欠点がある |
---|---|---|
IronPython の使用 | Python オペレーティング環境をインストールする必要がなく、強力な対話性、C# と Python 間のシームレスな接続 | numpy など、一部の Python サードパーティ ライブラリはサポートされていません |
C++ を使用して Python を呼び出し、C++ プログラムをダイナミック リンク ライブラリに作成します。 | 強力なインタラクティブ性 | ユーザーがPython環境を構築する必要があり、実装方法が複雑 |
C# コマンド ラインを使用して py ファイルを呼び出します | 高速実行 | ユーザーが Python 環境を構成する必要があり、対話性が低い |
呼び出し用にPythonファイルをexeにパッケージ化する | Python ランタイム環境をインストールする必要はありません。 | 実行速度が遅い、データ転送が複雑、対話性が低い |
4 つの方法には制限があり、強力な対話性、呼び出し可能なサードパーティの Python ライブラリ、ユーザーが同時に Python 環境を構成する必要がないという要件を満たすことが困難であることがわかります。これらの要件はまさに当てはまります。成熟したソフトウェアには必要です。pythonnet
ライブラリを利用することで上記の3つの要件を満たすことができます。
この記事は .net 6 環境でテストされています
パイソンネットを使用する
-
Nuget のインストール
pythonnet
-
Runtime.PythonDLL
プロパティ、つまり pythonxx.dll パスを設定します。xx はバージョン番号です。 -
設定
PythonEngine.PythonHome
、つまり python.exe が配置されているパス -
PythonEngine.PythonPath
Python スクリプトが配置されているディレクトリを設定します。複数のパスをセミコロンで区切って配置できますが、 pathToVirtualEnv\Lib\site-packages と pathToVirtualEnv\Lib を最後に配置する必要があります。 -
移行
PythonEngine.Initialize();
string pathToVirtualEnv = ".\envs\\pythonnetTest"; Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll"); PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe"); PythonEngine.PythonPath = $"{ pathToVirtualEnv}\\Lib\\site-packages;{ pathToVirtualEnv}\\Lib"; PythonEngine.Initialize(); //调用无参无返回值方法 using (Py.GIL()) //执行python的调用应该放在using (Py.GIL())块内 { //python对象应声明为dynamic类型 dynamic np = Py.Import("test"); np.hello(); } //调用有参有返回值方法 using (Py.GIL()) { dynamic np = Py.Import("test"); int r = np.add(1, 2); Console.WriteLine($"计算结果{ r}"); }
Python ファイルはPythonEngine.PythonPath
設定されたディレクトリに配置する必要があります
def hello():
print("hello")
def add(a,b):
return a+b
Python 環境を埋め込み、サードパーティのライブラリを使用する
このプログラムには、Python スクリプトとサードパーティ ライブラリに必要なすべての環境が含まれており、ユーザーをカスタム構成から節約できます。この記事では、Anaconda を使用して専用の仮想環境を構築します。
-
専用の仮想環境を作成し (Windows では、まず構築する仮想環境のルート ディレクトリに切り替えます)、
conda create --prefix=F:\condaenv\env_name python=3.7
必要に応じて実行パスと Python のバージョンをカスタマイズします。 -
Anaconda Prompt を使用して仮想環境をアクティブ化する
conda activate F:\condaenv\env_name
-
今回はサードパーティライブラリのNumpyをテストします(他のライブラリが必要な場合もインストール方法は同じです)、Numpyをインストールします
pip install numpy
string pathToVirtualEnv = ".\\envs\\pythonnetTest"; Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll"); PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe"); PythonEngine.PythonPath = $"{ pathToVirtualEnv}\\Lib\\site-packages;{ pathToVirtualEnv}\\Lib"; PythonEngine.Initialize() //使用第三方库 using (Py.GIL()) { dynamic np = Py.Import("numpy"); Console.WriteLine(np.cos(np.pi * 2)); dynamic sin = np.sin; Console.WriteLine(sin(5)); double c = (double)(np.cos(5) + sin(5)); Console.WriteLine(c); dynamic a = np.array(new List<float> { 1, 2, 3 }); Console.WriteLine(a.dtype); dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32); Console.WriteLine(b.dtype); Console.WriteLine(a * b); Console.ReadKey(); }
注意:C#和python对象进行数学运算时,必须将Python对象放到前面,例如np.pi*2,不能是2*np.pi
転送オブジェクト
C# オブジェクトを Python に渡すことができます
C# でオブジェクトを定義する
public class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName {
get; set; }
public string LastName {
get; set; }
}
string pathToVirtualEnv = ".\\envs\\pythonnetTest";
Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll");
PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe");
PythonEngine.PythonPath = $"{
pathToVirtualEnv}\\Lib\\site-packages;{
pathToVirtualEnv}\\Lib";
PythonEngine.Initialize();
//将C#中定义的类型传入python
using (Py.GIL())
{
Person p = new Person("John", "Smith");
PyObject pyPerson = p.ToPython();
string r1 = test.FullName(pyPerson);
Console.WriteLine($"全名:{
r1}");
}
python脚本
def FullName(p):
return p.FirstName+""+p.LastName
pydファイルを呼び出す
pyd ファイルには主に次の 2 つの機能があります。
- セキュリティの向上: pydで生成されたファイルはdllファイルとなり、ソースコードは閲覧できません。
- pydにコンパイルするとパフォーマンスが向上します
.py ファイルを pyd ファイルにコンパイルする手順は次のとおりです。
pip install cython
- .py ファイル ディレクトリに setup.py ファイルを作成します。
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = "testName",
ext_modules = cythonize("test.py"), #将test.py文件编译成pyd
)
- コンパイルコマンドを実行する
python setup.py build_ext --inplace
最終的に生成される pyd ファイルは通常、test+cpython バージョンです。プラットフォームはファイル名であり、名前をテスト名に変更することも、無視することもでき、使用時にテストによって引き続き呼び出すことができます。
pyd ファイルを動員することは py ファイルを呼び出すことと同じですが、実行効率が大幅に向上します。以下に実行速度を比較します。
実行速度の比較
test.py で時間のかかる関数を定義する
import time
def Count():
start = time.perf_counter()
sum = 0
for i in range(10000):
for j in range(10000):
sum = sum + i + j
print("sum = ", sum)
end = time.perf_counter()
runTime = end - start
runTime_ms = runTime * 1000
print("运行时间:", runTime, "秒")
- test.py スクリプトを直接実行すると、実行結果は次のようになります。
- C# で Conut() 関数を呼び出す
//运行时间测试
Console.WriteLine("C#开始计时");
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
test.Count();
stopWatch.Stop();
Console.WriteLine($"C#计时结束{
stopWatch.ElapsedMilliseconds}");
実行結果は以下の通りです。
pythonnet を使用して Python スクリプトを呼び出すと、ある程度のパフォーマンスが低下することがわかりますが、パフォーマンス要件がそれほど高くないという条件では許容されます。
- test.pyd ファイルを実行すると、結果は次のようになります。
この結果から、pyd の呼び出しはネイティブの py ファイルよりも高速であることがわかります。そのため、pythonnet を使用して pyd ファイルを実行できます。これにより、コードの保護が実現されるだけでなく、実行効率も向上します。