C# から Python を呼ぶ Pythonnet の使い方

# はじめに

機械学習や最適化、数値計算はPythonのライブラリがC#に対して非常に充実しているので、C#で簡単にできないことが多いしラッパーやC#むけに書き換えるのも時間がかかる。
そういった場合に役に立つライブラリ [Pythonnet](https://github.com/pythonnet/pythonnet) について紹介します。

このライブラリは、C#からPythonの関数を呼び出すことが出来る非常に便利なライブラリで.NET Foundationからもサポートを受けて開発されています。
PythonからC#を呼び出す機能も提供されていてそちらの方が記事が多いですが、C#からPythonを呼び出す記事はあまりなかったので備忘録的に作成しました。

# 型について

C#の型のままではPythonの関数に値を渡せないので、Python用の方に変換する必要があります。
基本的にはPyHoge型が存在してそれに変換します。

## 文字列

```cs
string str = "Foo";
var pyStr = new PyString(str);
```

## 整数型

```cs
int intNum = 1;
var pyInt = new PyInt(intNum);
```

## 倍精度浮動小数点型

```cs
double doubleNum = 4.5;
var pyFloat = new PyFloat(doubleNum);
```

## リスト

```cs
// C# のリストではなく Python（動的型付け） のリストなので
// List<T> のTような型指定はない
var strList = new PyList();

// Python のリストなので Add ではなく Append
list.Append(new PyString("foo"));
list.Append(new PyString("bar"));
list.Append(new PyString("baz"));

// 以下のようにも作れる
var pyObj = new PyObject[] {new PyInt(3), new PyInt(2), new PyInt(1) };
var pyList = new PyList(pyObj);
```

## 連想配列

```cs
var dict = new PyDict();
dict.SetItem("key1", new PyFloat(10.0));
dict.SetItem("key2", new PyFloat(20.0));
```

## 拡張メソッド

これまでPyHoge型をnewを使って作成する方法について書いてきたが、よく使う型については拡張メソッドが用意されているので、基本的にはC#そのままで書いていても問題なく変換できる。

```cs
//リストの例
var csList = new List<double>{1, 2, 3, 4};

// ToPython()メソッドがPythonに変換してくれる
PyObject py_list = csList.ToPython();

```


# ライブラリのインポート

単純なインポートなら以下のように書ける。

```py
import numpy
```
↓
```csharp
dynamic numpy = Py.Import("numpy")
```

from A import B の場合は以下のようにすればimportはできる。

```py
from math import radians
```
↓
```csharp
dynamic radians = Py.Import("math").GetAttr("radians");
```

もちろん以下のようにも書ける
```csharp
PyModule ps = Py.CreateScope();
ps.Exec(
    "from math import radians"
);
dynamic radians = ps.Get("radians");
```

ビルドインの関数(例えば`len()`)とかを使いたいときは以下

```csharp
dynamic py = Py.Import("builtins")

py.len("Hello, Python!"));  // 出力: 14
```

# 公式のサンプル
## C# で Numpy を使う例
既存のライブラリは簡単にC#側から呼ぶことが出来る。公式に記載されているC#からNumpyを呼び出す例は以下。

IDEのコードヒントは全くでないのでPython側のライブラリをよく把握していないとC#単体でPythonのコードをタイポなく呼ぶのは難しい。
だいたいの場合は一回Pythonで書いてからC#に書き換える方が楽な場合が多い

```cs
static void Main(string[] args)
{
    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();
    }
}

// Output
// 1.0
// -0.958924274663
// -0.6752620892
// float64
// int32
// [  6.  10.  12.]
```

## Pythonの関数をC#側で書いてC#側で呼ぶ方法

既存のライブラリを呼ぶまでもないものや、C#で書くと面倒なものは以下のように文字列としてC#上で書くとPythonコードとして使うことが出来る

```cs 
PythonEngine.Initialize();
using (Py.GIL())
{
    PyModule ps = Py.CreateScope();
    ps.Exec(
        "def add(a, b):\n" +
        "  return a + b\n" // Python を書いているのでインデントを忘れずに
    );

    // ここで上記で作成した add メソッドをC#側に取得している
    dynamic add = ps.Get("add");
    var result = add(1, 2)  // result = 3
}

```