// .NET ↔ Python in 4 lines
var executor = Python.GetInstance();
var temperatures = new[] { 23.5, 19.2, 31.8, 27.4, 22.1 };
using var result = executor.ExecuteAndCapture(@"
import statistics
result = {'mean': statistics.mean(data), 'stdev': statistics.stdev(data)}
", new Dictionary<string, object?> { { "data", temperatures } });
Console.WriteLine($"Mean: {result?.GetDouble("mean"):F1}°C ± {result?.GetDouble("stdev"):F1}");
요즈음은 원하든 원치 않든, 어떤 도메인이나 어떤 프로그래밍 언어를 쓰던 간에 Python과의 페어링이 어색하지 않은 시대입니다. 머신 러닝과 LLM이 대중화됨에 따라 더욱 명확해진 것 같습니다.
그러다보니 여러 프로그래밍 언어들이 Python과의 페어링과 콜라보에 대해 많은 고민을 하곤 합니다만, .NET의 경우에는 유독 그 속도가 느리다보니 아쉬운점이 많았습니다.
Pythonnet은 양방향 (C# → Python 및 Python → C#) 상호 운용성에는 충분히 검증되고 좋은 기술임이 틀림없지만, Deprecated 된 .NET Framework 시절의 Serialization에 기대거나, GIL을 호출자 쪽에서 관리하고 제어해야 한다는 불편한 점, 그리고 리플렉션에 많이 의존하다보니 최근 .NET 에코시스템에서 강조되는 AOT 지원이 불가하다는 한계가 뚜렷했습니다.
이런 문제를 해결하면서도 최신 스택인 uv 같은 도구와도 친밀성을 높여서 Python을 .NET의 First Citizen으로 만들어보고 싶다는 생각에서 출발하여 약 2~3개월간 고민하며 작업한 결과물을 오늘 0.5.0 버전으로 nuget 패키지로 처음 업로드합니다.
dotnetpy는 Python DLL이 드러내는 C API를 직접 P/Invoke로 불러오는 방식, 그리고 Python 측에서 반환하는 값을 JSON serialization을 통해서 회수하도록 설계하였습니다. 이 때, AOT에 대응될 수 있도록 JSON serialization은 리플렉션 기반이 아닌 JsonReader/JsonWriter 기반을 사용하도록 구현한 것이 차이점입니다.
이 Builders’ Log에는 앞으로 1.0 릴리스에 도달하기까지의 과정을 지속적으로 기록해보려 합니다. 많이 사용해보시고 많은 의견 주시면 감사하겠습니다!