PythonNET : Calling Python from C#
Intro
정문열 교수님의 '메타버스 설계 및 개발' 수업에서는 가상인간을 구현하기 위해 Unity와 python을 사용합니다.
해당 과정에서 가상인간과의 실시간 인터랙션을 위해 Unity의 C# 프로젝트와 Python 스크립트 간의 원활한 연결이 필수입니다. 대표적인 연결방법으로는 socket.io 방법이 있습니다.
하지만 https://github.com/Unity-Technologies/ml-agents/issues/3958에서 socket.io을 사용한 C#과 Python 간 통신은 성능에 주요 병목 현상을 야기하기 때문에 socket.io 대신 ‘Python 스크립트’를 ‘C# 프로젝트’에서 호출하는 PythonNET을 사용하는 것을 제안합니다.
다만, PythonNET을 사용하는 과정에서 환경 설정하는 방법이 친절하지 않아 오히려 혼란스러웠습니다.
PythonNET의 공식 문서에서도 특별히 도움이 되지 않았기 때문에, 저와 같은 상황에 처한 사람들을 위해 이 가이드를 쓰기로 결정했습니다.
Environment
os : Windows10
python 3.7.13 (최신 버전 X)
pythonnet 2.5.2 (최신 버전 X)
idle : visual studio 2019
pythonnet은 python3.6-3.8을 지원
Visual Studio 설치
저는 unity를 사용하기 때문에 unity hub에서 visual studio2019를 설치하였습니다.
아니면 아래 url을 통해 설치를 진행하면 됩니다.
https://learn.microsoft.com/ko-kr/visualstudio/releases/2019/release-notes
visual studio2019가 설치가 완료되면 visual studio installer에서 추가적으로 설치합니다.
anaconda 설치 및 가상 환경
python을 사용하다 보면 pip로 패키지를 설치하게 되는데 이 패키지들은 python 설치 폴더(디렉터리)의 Lib/site-packages 안에 저장됩니다. 그래서 pip로 설치한 패키지는 모든 python 스크립트에서 사용할 수 있게 됩니다. 평소에는 이런 방식이 큰 문제가 없지만 프로젝트를 여러 개 개발할 때는 패키지의 버전 문제가 발생합니다.
그래서 python 설치와 가상 환경을 관리하기 위해, https://www.anaconda.com/ 에서 anaconda를 설치합니다.
설치 시 'Add Anaconda to my PATH environment variable'는 꼭 선택하시기 바랍니다.
설치 후
anaconda prompt에서 다음과 같은 명령어를 입력합니다.
conda create -n sesac python=3.7.13
anaconda에서 ‘sesac’라는 이름으로 ‘3.7.13’의 python 버전 가상 환경을 만드는 명령어입니다.
conda activate sesac
가상 환경‘sesac’을 활성화합니다.
pip instal pythonnet==2.5.2
저는 pythonnet 2.5.2를 사용했기 때문에 다음과 같은 버전으로 설치하였습니다.
visual studio 설정 변경 및 빌드
새 프로젝트를 콘솔 앱(.NET Framework)을 선택하고 만듭니다. 콘솔 애플리케이션(x)
플랫폼 변경
C#프로젝트 우클릭 -> 속성 -> 빌드 탭 -> 플랫폼 대상 -> ‘x64’로 변경
Python.Runtime.dll 위치
참조를 하기 전에 Python.Runtime.dll의 위치를 알아야 합니다.
다음과 같이 pip uninstall을 하면 Python.Runtime.dll의 위치를 확인할 수 있습니다.
위치를 확인한 후 pip uninstall을 취소합니다.
Python.Runtime.dll 참조 추가
C#프로젝트 우클릭 -> 추가 -> 참조 -> 찾아보기 -> 위에서 찾은 Python.Runtime.dll 선택 -> 확인
Python.Runtime이 참조된 것을 확인할 수가 있습니다.
실행 예제 코드 https://github.com/Lim-YoungYoon/Pythonnet
C#
using System;
using System.Collections.Generic;
using Python.Runtime;
using System.IO;
namespace ConsoleApp1
{
class Program
{
// 시작 함수
static void Main(string[] args)
{
// 가상환경 경로
var pythonPath = @"C:\Users\dla12\anaconda3\envs\sesac";
// Python 홈 설정
PythonEngine.PythonHome = pythonPath;
// Python 엔진 초기화
PythonEngine.Initialize();
// Python을 불러오는 모든 코드는 아래의 블럭 안에 있어야 한다.
using (Py.GIL())
{
dynamic os = Py.Import("os"); // python에서의 'os'모듈을 import 하는 방법
dynamic sys = Py.Import("sys"); // 위와 동일
// 실행할 Python 파일 경로
var python_file_path = @"C:\Users\dla12\source\repos\PythonProject\python_file\pythonnet_test.py";
sys.path.append(os.path.dirname(os.path.expanduser(python_file_path)));
var fromFile = Py.Import(Path.GetFileNameWithoutExtension(python_file_path));
// pythonnet_test.py 에서 replace 메소드를 호출
fromFile.InvokeMethod("replace");
}
// python 환경을 종료한다.
PythonEngine.Shutdown();
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
추가적으로
- python을 불러오는 모든 코드는 "using (Py.GIL()) {/* CODE */}" 블록 안에 있어야 합니다.
- 모든 python 객체는 dynamic 타입으로 선언해야 합니다.
- 산술 연산을 할 때에는 python 객체를 먼저 써야 합니다. (np.pi * 2는 되고, 2 * np.pi는 안됩니다.)
python
import re
def replace():
print(re.sub(r"\s","*","Python is a programming langauge"))
Result
다음과 같이 ‘Python 스크립트’를 ‘C# 프로젝트’에서 호출이 됨을 확인할 수 있습니다.
etc
다음과 같은 에러 발생 시 해결 방법입니다.
가끔 파이썬을 실행하려고 하면 python37.dll 이 없어서 실행이 안 된다는 에러가 뜹니다.
설치한 가상 환경의 path에서 설치한 python의 버전 dll 파일을 찾아
C:\Windows\System32
C:\Windows\SysWOW64
위 두 경로에 복사해서 넣어주면 해결됩니다.
Reference
https://github.com/pythonnet/pythonnet
https://learn.microsoft.com/ko-kr/dotnet/api/system.io.path.getfilenamewithoutextension?view=net-7.0
https://somegenericdev.medium.com/calling-python-from-c-an-introduction-to-pythonnet-c3d45f7d5232
https://stackoverflow.com/questions/70401765/python-net-how-to-run-python-script-from-file-py
https://noteforstudy.tistory.com/m/entry/pythonnet-%EC%82%AC%EC%9A%A9%EB%B2%95
https://nowonbun.tistory.com/690
https://ireland-ireland.tistory.com/entry/C-Python-SystemDllNotFoundException-DLL-python37
https://www.youtube.com/watch?v=lqH4HNMPWUA