Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 6 Min.

Python in .NET – Integration mit Python.NET

Python-Code lässt sich in .NET-Anwendungen mit dem Open-Source-Projekt Python.NET einbinden. Wir erklären die Installation und grundlegende Interop-Szenarien. Ein einfaches Beispiel veranschaulicht die Praxis.
© EMGenie

Künstliche Intelligenz hält längst Einzug in produktive .NET-Anwendungen. Doch wie lassen sich in Python generierte Modelle und ML-Frameworks effizient in C#-Projekte integrieren? Der Artikel „Effizienter Brückenschlag“ aus der dotnetpro-Ausgabe 2/2026 [1] hat bereits eine Einführung in diese Thematik gegeben. Hier soll nun der Teilaspekt der Integration mittels Python.NET vertieft werden.

Python.NET besteht aus zwei Komponenten: dem C#-Wrapper (Python.Runtime.dll) und dem CPython-Interpreter. Zur Nutzung in .NET-Projekten fügen Sie das NuGet-Paket Python.Runtime hinzu. Das können Sie in einem aktiven .NET-Projekt zum Beispiel über den NuGet-Paketmanager (Bild 1) erledigen:

 

dotnet add package Python.Runtime 

Integration von Python.NET via NuGet in ein .NET-Projekt (Bild 1)

Integration von Python.NET via NuGet in ein .NET-Projekt (Bild 1)

© Autor

Auf der Python-Seite installieren Sie das PyPI-Paket mit:

 

pip install pythonnet

 

Achten Sie darauf, dass die Python-Version kompatibel ist: Python.NET 3.x unterstützt derzeit CPython bis Version 3.13. Ab Version 3.0 muss explizit auf die richtige Python-DLL verwiesen werden: Setzen Sie etwa Runtime.PythonDLL = "python38.dll" (Windows) oder PythonEngine.PythonHome/PYTHONHOME auf das Installationsverzeichnis. Für eine 64-Bit-Anwendung benötigen Sie die 64-Bit-Python-Version.

Python aus C# aufrufen (Einbettung)

In C# initialisieren Sie die Python-Engine und rufen Python-Module wie folgt auf:

 

using Python.Runtime;
static void Main(string[] args)
{
    // Python-Interpreter initialisieren
    PythonEngine.Initialize();    
    // Alle Python-Aufrufe in einem using-Block mit GIL
    using (Py.GIL())
    {
        // Beispiel: Unser Python-Script heißt "pythonExample.py"
        dynamic mod = Py.Import("pythonExample");    // Importiert das Python-Modul
        dynamic calc = mod.Calculator();   // Erstellt Instanz der Klasse Calculator
        int result = calc.Add(7, 9);      // Ruft die Methode Add auf
        Console.WriteLine($"Ergebnis: {result}");
    }
    PythonEngine.Shutdown();  // Interpreter ordentlich herunterfahren
}

 

Erläuterung: Zuerst wird PythonEngine.Initialize() aufgerufen. Danach nutzen wir using (Py.GIL()) { … }, um den Zugriff auf den Python-Interpreter zu halten. Innerhalb der using-Anweisung importieren wir das Python-Skript (pythonExample.py) über Py.Import("pythonExample"). Python-Objekte sind hier vom dynamischen Typ dynamic, was die Funktionsaufrufe vereinfacht. Die in Python berechneten Ergebnisse können C#-Variablen zugewiesen werden (zum Beispiel wird result zu int gecastet). Abschließend rufen wir PythonEngine.Shutdown() auf, um Ressourcen freizugeben. Der Datenfluss ist in Bild 2 dargestellt.

Flussdiagramm des Aufrufs zwischen .NET (C#) und Python über Python.NET (Bild 2)

Flussdiagramm des Aufrufs zwischen .NET (C#) und Python über Python.NET (Bild 2)

© Autor

Diese Einbettung ermöglicht den direkten Zugriff auf beliebige Python-Bibliotheken, etwa NumPy, Scikit-learn und viele weitere. Auch dazu ein Beispiel (Prinzip):

 

using (Py.GIL())
{
    dynamic np = Py.Import("numpy");
    double c = (double)np.cos(np.pi * 2);
    Console.WriteLine(c);
}

 

Soll pythonExample.py nicht im Projektverzeichnis liegen, können Sie dynamic sys = Py.Import("sys"); sys.path.append("…") verwenden, um den Suchpfad anzupassen. Beachten Sie, dass Python-Code in .NET nicht von der CLR verifiziert wird und daher nur vertrauenswürdiger Code ausgeführt werden sollte.

C#-Code aus Python aufrufen

Python.NET ermöglicht es auch, .NET-Assemblies in Python zu verwenden, beispielsweise wie folgt:

 

import clr
clr.AddReference("MyDotNetLibrary")          # DLL-Namen ohne ".dll"
from MyNamespace import MyClass              # C#-Klasse importieren
obj = MyClass()                              # Instanz erzeugen
print(obj.MyMethod(5))                       # Methode aufrufen

 

Hier übernimmt das clr-Modul die Brücke: clr.AddReference lädt eine .NET-Assembly.

 

from pythonnet import load
load("coreclr")

 

Danach steht der gesamte .NET-Namensraum wie ein Python-Paket zur Verfügung. Diese Richtung („.NET aus Python“) wird oft genutzt, um .NET-Funktionen in Python-Skripten zu nutzen, sofern man beispielsweise ein bestehendes C#-Modul verwenden möchte.

Fallstricke, Debugging und Performance

Häufige Stolpersteine sind:

  • das Vergessen des GIL, das heißt, außerhalb von using (Py.GIL()) darf kein Python-Code ausgeführt werden
  • Architektur-Mismatches (x86 versus x64)
  • fehlende Python-Abhängigkeiten, das heißt, es muss die Runtime.PythonDLL gesetzt werden, zum Beispiel für die python310.dll

Um in Python zu debuggen, können Sie die Entwicklungsumgebungen beziehungsweise Editoren PyCharm, VS Code oder Visual Studio (mit Python-Tools) einsetzen. Setzen Sie zur Laufzeit zum Beispiel Breakpoints oder print-Anweisungen in Ihrem Python-Skript.

Interop-Aufrufe sind relativ „teuer“, das heißt, jeder Aufruf wird über den Bridge-Layer vermittelt. Die Nutzung von Python-Funktionen aus C# heraus ist deshalb deutlich weniger performant, als wenn man direkt C#-Code nutzen würde. Das ist insbesondere bei häufigen und wiederholenden Aufrufen (Verarbeitung in Schleifen) von Bedeutung. Der Overhead entsteht durch Dynamik, GIL-Akquise und Marshalling, wobei Marshalling das Umwandeln von strukturierten oder elementaren Daten in ein Format bedeutet, das die Übermittlung an andere Prozesse oder Programme ermöglicht.

Ein praktikabler Ansatz ist es daher, möglichst große Datenmengen oder Aufrufe zu bündeln (Batch-Query). Python-intensive Algorithmen sollten möglichst vollständig in Python stattfinden. Beachten Sie außerdem: Python.NET nutzt den CPython-Interpreter mit Global Interpreter Lock (GIL), daher laufen Python-Aufrufe stets seriell.

Beim Deployment gilt es sicherzustellen, dass die Python-Laufzeitumgebung auf dem Zielsystem vorhanden ist (oder eingebettet wird). Üblich ist, eine konkrete Python-Installation mitzuliefern und die Systemvariable PythonEngine.PythonHome auf dieses Installationsverzeichnis zu setzen. Achten Sie auf alle benötigten DLLs (Windows) und die korrekte Architektur (32/64 Bit). Abschließend testen Sie die Verteilung im Zielsystem.

Vergleich mit Alternativen

Python.NET ist zwar der flexibelste und leistungsfähigste Ansatz zur direkten Integration von Python in .NET-Anwendungen, jedoch keineswegs die einzige Möglichkeit, Python-Funktionalität aus C# heraus zu nutzen. Je nach Einsatzszenario können auch alternative Integrationsstrategien sinnvoll sein. Eine naheliegende Alternative ist IronPython, eine vollständig in .NET implementierte Python-Laufzeitumgebung. Da IronPython ohne nativen CPython-Interpreter arbeitet, entfällt die Abhängigkeit von einer externen Python-Installation. Zudem besitzt IronPython keinen Global Interpreter Lock (GIL), wodurch echte parallele Ausführung mehrerer Threads möglich ist. Das ist ein Vorteil gegenüber CPython-basierten Lösungen wie Python.NET. Allerdings hat dieser Ansatz Einschränkungen: IronPython unterstützt nur ältere Python-Versionen und kann keine nativen C-Erweiterungen laden. Damit entfallen viele zentrale Bibliotheken des modernen Python-Ökosystems. Hinzu kommt, dass die Weiterentwicklung des Projekts deutlich langsamer verläuft als bei Python.NET.

Ein weiterer pragmatischer Ansatz besteht darin, Python-Skripte als externen Prozess aus einer .NET-Anwendung heraus zu starten, beispielsweise über Process.Start("python", "script.py"). Diese Methode ist einfach umzusetzen und nutzt eine vollständig normale Python-Installation, ohne spezielle Interop-Bibliotheken zu benötigen. Dadurch eignet sich der Ansatz insbesondere für lose gekoppelte Batch- oder Automatisierungsszenarien. Der Nachteil liegt jedoch im vergleichsweise hohen Overhead: Jeder Aufruf startet einen neuen Prozess, was zusätzliche Latenz verursacht und den Ansatz für häufige oder echtzeitkritische Aufrufe ungeeignet macht. Zudem ist die Kommunikation zwischen .NET und Python hierbei deutlich umständlicher, da Daten typischerweise über Dateien, Kommandozeilenparameter oder Standard-Ein-/Ausgabe übertragen werden müssen.

Um die Startkosten eines Subprozess-Ansatzes zu reduzieren, kann ein persistenter Python-Prozess eingesetzt werden, der dauerhaft im Hintergrund läuft und über Standard-Ein-/Ausgabe oder Sockets mit der .NET-Anwendung kommuniziert. Dieses Muster wird häufig als „Python as a Service“ bezeichnet und eignet sich besonders für performantere Integrationsszenarien, bei denen dennoch eine Prozessentkopplung gewünscht ist. Subprozess-Lösungen sind eine pragmatische Wahl für lose gekoppelte Skriptausführung oder Batch-Verarbeitung. Python.NET bleibt hingegen die beste Option, wenn der vollständige Zugriff auf das moderne CPython-Ökosystem erforderlich ist und Python-Code eng in die .NET-Anwendung integriert werden soll. Es dürfte der passende Ansatz für die meisten Anforderungen sein, denn es sind vollständige Python-Bibliotheken verfügbar.

Fazit

Mit Python.NET lassen sich .NET- und Python-Welten praktisch verschmelzen. Einmal korrekt eingerichtet, können Sie in C# auf beliebige Python-Funktionen und -Pakete zugreifen und so zum Beispiel ML-Modelle in bestehende .NET-Anwendungen einbetten. Die größten Hürden sind Konfigurationsdetails (Python-Pfad, GIL-Management) und Performance-Kosten, die aber durch sinnvolles Design kompensiert werden können. Die Integration eröffnet C#-Entwicklern viele Wege, von der reichen Python-Ökonomie zu profitieren.

[1] Veikko Krypczyk, Effizienter Brückenschlag, dotnetpro 2/2026, Seite 40 ff.

Neueste Beiträge

libSQL und Turso: SQLite für verteilte Systeme - SQLite für .NET-Entwickler, Teil 3
libSQL und Turso lösen die größte Einschränkung von SQLite: die Bindung an eine einzelne Instanz.
6 Minuten
15. Apr 2026
Wissen trifft Community - DWX 2026
Wohlfühlzeit mit intensivem Lernen für Entwickler:innen auf der DWX in Mannheim.
9 Minuten
16. Apr 2026
ROI von KI
Wie Entwickler die Lücke bei der Bereitschaft, KI einzusetzen, schließen können.
7 Minuten

Das könnte Dich auch interessieren

SignalRC und Ping - Der DDC-Truck, Teil 10
Wie schnell ist die Verbindung zwischen Browser und Fahrzeug eigentlich?
9 Minuten
26. Mär 2026
SignalRC baut auf DRY - Der DDC-Truck, Teil 8
DRY ist eines dieser Prinzipien, die jeder für selbstverständlich hält, die aber trotzdem oft nicht konsequent umgesetzt werden. In SignalRC ist das Shared-Projekt von Beginn an dabei.
11 Minuten
12. Mär 2026
SignalRC mit ReactFlow – alles im Fluss - Der DDC-Truck, Teil 9
ReactFlow verwandelt ein abstraktes Signalverarbeitungsproblem in etwas, das man buchstäblich sehen und anfassen kann. Dabei ist die Signalverarbeitungskette vollständig datengetrieben.
13 Minuten
19. Mär 2026
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige