임영윤

PythonNET : Calling Python from C#

1devlim 2022. 12. 9. 00:40

Intro

정문열 교수님의 '메타버스 설계 및 개발' 수업에서는 가상인간을 구현하기 위해 Unitypython을 사용합니다.

해당 과정에서 가상인간과의 실시간 인터랙션을 위해 UnityC# 프로젝트와 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

 

pythonnetpython3.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를 설치합니다.

Advaced Options

 

설치 시 '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)

콘솔 앱(NET.Framework)

플랫폼 변경

C#프로젝트 우클릭 -> 속성 -> 빌드 탭 -> 플랫폼 대상 -> ‘x64’로 변경


Python.Runtime.dll 위치

참조를 하기 전에 Python.Runtime.dll의 위치를 알아야 합니다.

다음과 같이 pip uninstall을 하면 Python.Runtime.dll의 위치를 확인할 수 있습니다.

위치를 확인한 후 pip uninstall을 취소합니다.


Python.Runtime.dll 참조 추가

C#프로젝트 우클릭 -> 추가 -> 참조 -> 찾아보기 -> 위에서 찾은 Python.Runtime.dll 선택 -> 확인

Python.Runtime.dll  참조 방법

Python.Runtime이 참조된 것을 확인할 수가 있습니다.

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 로드할 수 없습니다.

가끔 파이썬을 실행하려고 하면 python37.dll 이 없어서 실행이 안 된다는 에러가 뜹니다.

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