Python PythonNet === ###### tags: `Python` > * 紀錄如何在 Python 使用 PythonNet 呼叫 .net framework dll (C#, C++/cli) > * 若要在 C# 中透過 PythonNet 呼叫 python .pyd 可參考 [C# PythonNet](https://hackmd.io/@AvalonChang/H1idvcYZJl) ## :package: **安裝 pythonnet package** * PythonNet 可透過 pip install 或 conda install 等方式安裝至虛擬環境 >安裝完成後可在 python 內 import clr 測試, 若可正常 import 代表安裝成功 ```python! import clr ``` --- ## :computer: **調用 dll** ### 1. 添加 dll 路徑 若 dll 所在路徑不在當前 python 的工作目錄中, 建議直接將 dll 路徑加入到 Python 工作目錄中 > 若 dll 有相依其他 dll , 但相依 dll 不在 Python 工作目錄中也會導致程式報錯 ```python! import os import sys # 將 DLL 所在目錄加入 Python 工作目錄 dll_dir = 'your_dll_directory' if dll_dir not in sys.path: sys.path.append(dll_dir) ``` ### 2. 將 dll 加入 clr 的 reference 中 由於剛才已將 dll 所在目錄加入工作目錄中, 這裡的 'your_dll_name' 可以是不含路徑和副檔名的檔案名稱 > 這裡也可以直接填入完整的 dll 路徑, ```python! clr.AddReference('your_dll_name') ``` ### 3. import 所需的 dll 類別 * 假設 dll 內有個名為 "XXX_sapce" 的 namespace, 想要呼叫其中的 class XXX_class, 在 python 中就可以這麼做 ```python! from XXX_space import XXX_class xxx = XXX_class() # 假設 constract 時不需要輸入參數 ``` * 若 dll 內有很多需要使用的功能, 也可以一次將 dll 內容全部 import ```python! from XXX_space import * xxx = XXX_class() yyy = YYY_class() ``` ## :memo: Note 經過上述, 基本上已經確定能導入 dll 功能在 Python 中使用 但由於程式語言的差異, 有些語法或變數結構無法直接對應, 這邊僅列出遇過的情況 ### a. 變數結構 在 .net framework 中有 System.Int32, System.String 等變數結構 在 Python 中也可以透過 import System 的方式使用 ```python! from System import String, Int32, Array, Byte # etc, ... ``` 同理, 當 dll interface 需要 .net List 的結構時就可以在 Python 中宣告出 .net framework 的結構 ```python! from System.Collections.Generic import List dot_net_str_list = List[String]() # 宣告一個 .net framework 的 string list dot_net_str_list.Add('something_you_need') # 如同 python list 的 append xxx.func(dot_net_str_list) # 呼叫一個需要 .net framework string list 的 function ``` ### b. call by reference * 在 C# 中存在 call by reference 的語法 ```csharp! void call_by_ref(ref int value) { value = -1; } void main() { int val = 0; call_by_ref(val); Console.WriteLine(val); // 輸出會是 -1 } ``` 但在 Python 中會得到下列結果 ```python! val = 0 call_by_ref(val) print(val) # 輸出會是 0 ``` 這是由於 Python 傳遞參數的方式是 call by sharing, 對於 int 這類不可變類型會是類似 call by value 的處理方式 * 為了解決這個問題, PythonNet 會將修改後的 reference 以 return 的方式回傳 ```python! val = 0 val = call_by_ref(val) print(val) # 輸出會是 -1 ``` > 若函數本來就有回傳值, 或是不只一個 ref 參數 > 則會以 tuple 的方式回傳 ---