Edited at

Pythonから.NETを呼び出す方法とついでにその逆も

More than 1 year has passed since last update.


はじめに

Pythonで開発をしている中で一部.Net向けに提供されているDLLを呼び出す必要があり,その方法について調べました.

Pythonと.NETというとIronPythonが思い浮かびますが,今回はふつうにCPythonから使えるPython for .NETを採用しました.


Python for .NET

今回の目的そのままですが,Pythonのコードから.NETのコードを呼び出すことができます.

またその逆に,C#のコードからPythonを呼び出すこともでるようです.(Python for .NETって名前からはこっちのほうがしっくりくる??)


開発環境

今回試した開発環境です.


  • Windows 10 Pro

  • Python 3.6.5

  • Visual Studio Community 2017 15.6.6


Pythonのコードから.NETを呼び出す

まずはもともとの目的の,Pythonのコードから.NETを呼び出す方法をです.


インストール

pipからインストールできます.

> pip install pythonnet


標準のクラスライブラリを呼び出す

試しにFormアプリケーションのエントリーポイントの記述をまねて書いてみます.

clr.AddReferenceで必要なアセンブリを読み込み,.NETの名前空間をPythonのパッケージとしてimportして扱うことができます.

import clr

clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Application, Form

Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(Form())

image.png

ジェネリックスは角括弧を使って書くことができます.以下はListクラスの使用例です.

import clr

clr.AddReference('System.Collections')
from System.Collections.Generic import List

lst = List[int]()
lst.Add(3)
lst.Add(2)
lst.Add(1)
lst.Sort()

for x in lst:
print(x)


実行結果

1

2
3


自作のコードを呼び出す

次は自作のクラスをPythonコードから呼び出します.C#のコードはサンプルとして以下のコードを使いました.

using System;

namespace MyClassNamespace
{
public class Foo
{
private string text;
public Foo(string text) => this.text = text;
public void DoSomething(int count) => Console.WriteLine(new string('*', count).Replace("*", text));
public void DoSomething(string prefix) => Console.WriteLine($"{prefix}: {text}");
}
}

上記のコードをビルドしたMyClass.dllをPythonのコードと同じディレクトリにコピーすると,以下のように読み込めます.

オーバーロードされているメソッドはOverloads[type]で呼び分けることができます.(ただし今回の例だと明示的に指定しなくてもうまく動作しました.)

import clr

clr.AddReference('MyClass')
from MyClassNamespace import Foo

foo = Foo('ABC')
foo.DoSomething.Overloads[int](3)
foo.DoSomething.Overloads[str]('@@@')


実行結果

ABCABCABC

@@@: ABC


C#のコードからPythonを呼び出す

もともとの目的とは違うのですが,逆にC#側からPythonのコードを呼び出す方法についても簡単にまとめます.


インストール

NuGetからインストールできます.ただPython 3.5までの対応のようです.

image.png


Pythonのモジュールを呼び出す

GitHubのREADMEに紹介されていたコードです.

ですが自分の環境ではやはり動きませんでした.またPython 3.5が入った環境で動かす機会があれば追記します.

using System;

using System.Collections.Generic;
using Python.Runtime;

namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
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 = 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();
}
}
}
}


実行結果

ハンドルされていない例外: System.DllNotFoundException: DLL 'python35' を読み込めません:指定されたモジュールが見つかりません。 (HRESULT からの例外:0x8007007E)

場所 Python.Runtime.Runtime.Py_IsInitialized()
場所 Python.Runtime.Runtime.Initialize()
場所 Python.Runtime.PythonEngine.Initialize(IEnumerable`1 args, Boolean setSysArgv)
場所 Python.Runtime.PythonEngine.Initialize(Boolean setSysArgv)
場所 Python.Runtime.PythonEngine.Initialize()
場所 Python.Runtime.Py.GIL()