Execute Python Code from C# with PythonNET

Programming Python C#

How to bridge the two worlds in a few lines of code

If you’re a C# developer who needs to tap into a Python library, run a script, or simply call a helper function written in Python, the PythonNET library is one of the simplest ways to do it. It exposes the CPython interpreter to .NET, allowing you to import modules, instantiate classes, and call functions as if they were native C# objects.

Get the NuGet package

Open your project in Visual Studio (or VS Code with the C# extension) and install the Python‑NET package:


Prepare the Python environment

Either you set a system environment var via MS Windows


or you do it explicit within C# like:

string pythonDll = @"C:\Users\YourUserNameHere\AppData\Local\Programs\Python\Python311\python311.dll";
Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", pythonDll);

otherwise you will get a runtime error like


Make sure the Python DLLs (for example python311.dll on Windows, libpython3.11.so on Linux when using Python ver 3.11) are in a directory that the OS can find (e.g., the same folder as your executable or in the system PATH) or specify the whole path.

Calling Python Libraries from C#

NumPy is a Python library used for working with arrays.  It also has functions for working in domain of linear algebra, fourier transform, and matrices.  To use this python library in C# all we need to do is:

using Python.Runtime;

namespace PythonNetDemo1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            PythonEngine.Initialize();
            using (Py.GIL())
            {
                // module must be installed first via "pip install numpy" in cli if you haven't done so
                dynamic np = Py.Import("numpy");
                Console.WriteLine(np.cos(np.pi * 2));

                dynamic sin = np.sin;
                Console.WriteLine(sin(5));

                double c = (double)(np.cos(5) + sin(5));
                Console.WriteLine(c);

                dynamic a = np.array(new List<float> { 1, 2, 3 });
                Console.WriteLine(a.dtype);

                dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
                Console.WriteLine(b.dtype);

                Console.WriteLine(a * b);
                Console.ReadKey();
            }
        }
    }
}

and the C# console application will output

Execute a Python script

You have a *.py script that you need to execute from C# - no problem.  Let's say we have this Python script galacticMiner.py:

from typing import List

class Miner:
    def extractMaxYield(self, yields: List[int]) -> int:
        """
        This function calculates the maximum total mineral yield that can be extracted from a sequence of asteroids given an array of mineral yields.
        
        Parameters:
        yields (List[int]): A list of integers representing the mineral yield of each asteroid in the sequence, where yields[i] is the yield of the i-th asteroid.
        
        Returns:
        int: The maximum total mineral yield that can be extracted from the given asteroid sequence while adhering to the energy recharge restrictions.
        """
        n = len(yields)
        if n == 0:
            return 0
        if n == 1:
            return yields[0]
        
        # Create an array to store the maximum yield at each index
        dp = [0] * n
        
        # Base cases
        dp[0] = yields[0]
        dp[1] = max(yields[0], yields[1])
        
        # Fill the array
        for i in range(2, n):
            dp[i] = max(dp[i-1], dp[i-2] + yields[i])
        
        # The last element of the array will contain the maximum yield
        return dp[-1]

# Example usage:
# miner = Miner()
# print(miner.extractMaxYield([6, 5, 5, 7, 4]))   # expected output: 15
# print(miner.extractMaxYield([1, 5, 3]))         # expected output: 5
# print(miner.extractMaxYield([4, 4, 4, 4]))      # expected output: 8

then all we need is this C# code in order to execute the script (include it in your VS solution and set the build action to "copy"):

using Python.Runtime;

namespace PythonNetDemo2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // when PythonEngine.Initialize() throws an error:
            //    string pythonDll = @"C:\Users\YourUserNameHere\AppData\Local\Programs\Python\Python311\python311.dll";
            //    Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", pythonDll);

            PythonEngine.Initialize();
            using (Py.GIL())
            {
                // append current directory
                dynamic sys = Py.Import("sys");
                sys.path.append(Directory.GetCurrentDirectory());

                // Import the Python file
                dynamic minerModule = Py.Import("galacticMiner");

                // Create an instance of the Miner class
                dynamic miner = minerModule.Miner();

                // Call the extractMaxYield() method 3 times
                int[] result =
                [
                    miner.extractMaxYield(new List<int> {6, 5, 5, 7, 4 }),  // should be 15
                    miner.extractMaxYield(new List<int> { 1, 5, 3 }),       // should be 5
                    miner.extractMaxYield(new List<int> { 4, 4, 4, 4 })     // should be 8
                ];

                // display the results
                for (int i = 0; i < 3; i++)
                    Console.WriteLine($"Result {i+1}: {result[i]}");
            }
        }
    }
}

and we get

Dynamic Python Code Execution

We can also dynamically execute Python code on the fly - may it be a simple one liner or even a class like this calculator:

using Python.Runtime;

namespace PythonNetDemo3
{
    internal class Program
    {
        static bool init = false;

        static void Main(string[] args)
        {
            // simple print
            RunPythonCode(@"print(""hello world from python!"")");

            // consume our simple python Calculator class
            RunPythonCode(
@"class Calculator:
  def AddInPython(self, a, b):
    return a + b
calc = Calculator();
print(calc.AddInPython(2, 3))
");
        }

        public static void InitializePython()
        {
            if (!init)
            {
                // when PythonEngine.Initialize() throws an error:
              //    string pythonDll = @"python311.dll";
                //    Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", pythonDll);
                PythonEngine.Initialize();
                init = true;
            }
        }

        // dynamically run python code
        public static void RunPythonCode(string pyCode)
        {
            InitializePython();
            using (Py.GIL())
            {
                PythonEngine.RunSimpleString(pyCode);
            }
        }
    }
}

which will result in

Be aware to follow the Python style guides for intention when dealing with dynamic Python code!

Download

You can download those 3 sample apps here.

Conclusion

With these steps, you can quickly embed Python logic in your C# applications—whether you’re calling a small helper function, leveraging a full-featured data‑science library, or running a legacy Python script.

Happy coding, and enjoy the best of both worlds!