Pythonnet – A Simple Union of .NET Core and Python You’ll Love

Python is a powerful and versatile programming language that has become increasingly popular. For many, it’s one of the very first programming languages they pick up when getting started. Some of the highest traffic posts on my blog many years after they were written look at using C# and Python together. Today we’re going to explore how you can use Python from inside a C# .NET Core application with much more modern approaches than my original articles. Enter Pythonnet!

Pythonnet Package & Getting Started

We’re going to be looking at Python for .NET in order to accomplish this goal. This library allows you to take advantage of Python installed on the running machine from within your .NET Core applications. You must configure it to point at the corresponding Python DLL that you’d like to use, and after a couple of lines of initialization you’re off to the races!

Example 1 – Hello World with Pythonnet

To get started, you’ll need to install the pythonnet package from NuGet. Once you’ve done that, you can use the following code to run a Python script from your C# code:

using Python.Runtime;

internal sealed class Program
{
    private static void Main(string[] args)
    {
        // NOTE: set this based on your python install. this will resolve from
        // your PATH environment variable as well.
        Runtime.PythonDLL = "python310.dll";

        PythonEngine.Initialize();
        using (Py.GIL())
        {
            using var scope = Py.CreateScope();
            scope.Exec("print('Hello World from Python!')");
        }
    }
}

This code sets our python DLL path on the Runtime, which will be a necessary step. Don’t forget to do this! We must then call PythonEngine.Initialize() and Py.GIL(), which we will want to dispose of later so consider a using statement. We can ask the static Py class to create a scope for us to use, and then leverage the Exec method in order to execute some Python code. In this example, we’re calling the Exec method to run a simple Python script that prints “Hello World from Python!” to the console.

Example 2 – A Pythonnet Calculator!

You can also use the Python C API to call Python functions directly from C# code. To do this, you’ll need to create a C# wrapper for the Python function you want to call. Here’s an example of how you might create a wrapper for a Python function that takes two integers as arguments and returns their sum:

using System;
using Python.Runtime;

internal sealed class Program
{
    private static void Main(string[] args)
    {
        // NOTE: set this based on your python install. this will resolve from
        // your PATH environment variable as well.
        Runtime.PythonDLL = "python310.dll";

        PythonEngine.Initialize();
        using (Py.GIL())
        {
            // NOTE: this doesn't validate input
            Console.WriteLine("Enter first integer:");
            var firstInt = int.Parse(Console.ReadLine());

            Console.WriteLine("Enter second integer:");
            var secondInt = int.Parse(Console.ReadLine());

            using dynamic scope = Py.CreateScope();
            scope.Exec("def add(a, b): return a + b");
            var sum = scope.add(firstInt, secondInt);
            Console.WriteLine($"Sum: {sum}");
        }
    }
}

In this example, we’re using the Exec method to define a Python function called “add” that takes two integers as arguments and returns their sum. Thanks to the dynamic keyword in C#, we are able to make an assumption that our scope object has a method called “add” directly on it. Finally, we use C# code directly to call the “add” method just like as if it was inside of C#. The return type assigned to the “sum” variable in C# is also dynamic, but you could declare this variable as an integer and it will compile properly with this type as well.

A calculator output using Pythonnet
The results in the console window from our simple Pythonnet calculator!

Example 3 – Object Interop

We can also pass Python objects to C# functions and vice versa. Here’s an example of how you might receive a Python list back to C# function and iterate over its elements without using the dynamic keyword:

using Python.Runtime;

internal sealed class Program
{
    private static void Main(string[] args)
    {
        // NOTE: set this based on your python install. this will resolve from
        // your PATH environment variable as well.
        Runtime.PythonDLL = "python310.dll";

        PythonEngine.Initialize();
        using (Py.GIL())
        {
            using var scope = Py.CreateScope();

            scope.Exec("number_list = [1, 2, 3, 4, 5]");
            var pythonListObj = scope.Eval("number_list");
            var csharpListObj = pythonListObj.As<int[]>();

            Console.WriteLine("The numbers from python are:");
            foreach (var value in csharpListObj)
            {
                Console.WriteLine(value);
            }

            Console.WriteLine("Press enter to exit.");
            Console.ReadLine();
        }
    }
}

In this example, we’re using the Exec method to create a Python list and assign it to a variable called “number_list”. We then use the Eval method to get a reference to the list object and then we can call As<T> with an integer array (denoted as int[]) to convert the result. At this point, csharpListObj is a fully functional C# array with the array of integers from Python. And to prove it, we can list them all to the console!

A list output using Pythonnet
The numbers printed to the console are declared in Python and passed back to C# from Pythonnet

Summary

In conclusion, using Python inside C# .NET Core application is easy and seamless. Python for .NET provides many methods for interacting with the Python interpreter and calling Python functions. By using these methods, you can leverage the power of Python to add new functionality in your C# .NET Core applications. What are you going to build?!

You can check out more of my content by visiting the following links:

YouTube: https://youtube.com/@DevLeader
TikTok: https://www.tiktok.com/@devleader
Blog: https://www.devleader.ca
GitHub: https://github.com/ncosentino/
Twitch: https://www.twitch.tv/ncosentino
Twitter: https://twitter.com/DevLeaderCa
Facebook: https://www.facebook.com/DevLeaderCa
Instagram: https://www.instagram.com/dev.leader
LinkedIn: https://www.linkedin.com/in/nickcosentino

Nick Cosentino

Principal Software Engineering Manager at Microsoft. Views are my own.

Leave a Reply