Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
First steps with Python.NET
#1
The PythonNET documentation available on the web can be frustratingly opaque. This example is cribbed from the one at pythonnet.github.io with significant tweaking.
 
Code:
Copy      Help
/*/ nuget PyNet\pythonnet; /*/
using System;
using Python.Runtime;

// create a person object
Person person = new Person("Engelbert", "Humperdinck");

// If you do not have PYTHONNET_DLL set, uncomment the line below and
// modify the path to suit your system.
// Runtime.PythonDLL = @"C:\Program Files\Python310\python310.dll";
// I don't require that line on my system.

PythonEngine.Initialize();
// acquire the GIL before using the Python interpreter
using (Py.GIL())
{
    // create a Python scope
    using dynamic scope = Py.CreateScope();
    {
        // convert the Person object to a PyObject
        PyObject pyPerson = person.ToPython();

        // create a Python variable "person"
        scope.Set("person", pyPerson);

        // the person object may now be used in Python
        scope.Exec("fullName = person.FirstName + ' ' + person.LastName");
        var pythonStrObj = scope.Eval("fullName");
        var csharpStrObj = pythonStrObj.As<string>();
        print.it(csharpStrObj );
    }
}
PythonEngine.Shutdown();

/// <summary>
/// C# Person class
/// </summary>
public class Person
{
    /// <summary>
    ///
    /// </summary>
    /// <param name="firstName"></param>
    /// <param name="lastName"></param>
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    /// <summary>
    /// In the Python code, use FirstName
    /// </summary>
    public string FirstName { get; set; }
    /// <summary>
    /// In the Python code, use LastName
    /// </summary>
    public string LastName { get; set; }
}

Here's another example, this time converting a Python list object to a C# int[]. I got the original example from this CodeProject post, and it was a little easier to mod.
 
Code:
Copy      Help
/*/ nuget PyNet\PythonNet; /*/
using Python.Runtime;

PythonEngine.Initialize();
using (Py.GIL()) // begin 'using'
{
    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[]>();

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

} // end 'using'.
PythonEngine.Shutdown();
#2
Another example.
  1. Get first number from the user (as string).
  2. Convert to double.
  3. Get second number from the user (as string).
  4. Convert to double.
  5. Create Python scope.
  6. In scope, define a Python function 'add'.
  7. Create C# var 'sum', equal to the return value of scope.add(firstNumber, secondNumber).
  8. Print 'sum'.
 
Code:
Copy      Help
/*/ nuget PyNet\PythonNet; /*/
using Python.Runtime;

PythonEngine.Initialize();
using (Py.GIL()) {
    // NOTE: this doesn't validate input
    if (!dialog.showInput(out string firstCsharpNumber, "Enter first number:")) return;
    PyObject firstNumber = Convert.ToDouble(firstCsharpNumber).ToPython();
    
    if (!dialog.showInput(out string secondCsharpNumber, "Enter second number:")) return;
    PyObject secondNumber = Convert.ToDouble(secondCsharpNumber).ToPython();
    
    dynamic scope = Py.CreateScope();
    scope.Exec("def add(a, b): return a + b");
    var sum = scope.add(firstNumber, secondNumber);
    print.it("Sum: " + sum);
}
PythonEngine.Shutdown();
#3
Insert before PythonEngine.Initialize():
 
Code:
Copy      Help
Runtime.PythonDLL = @"C:\Program Files\Python311\python311.dll";

Or set environment variable PYTHONNET_PYDLL.

If Python not installed, download from https://www.python.org/downloads/
#4
@Gintaras, thank you. I should have mentioned that my PYTHONNET_DLL is already set. Many of the examples I've looked at have the line you posted (or similar) in the code. OP has been updated.
Regards,
burque505
#5
The script process does not exit. Finally need to call
 
Code:
Copy      Help
PythonEngine.Shutdown();
#6
This class makes it simpler.
 
Code:
Copy      Help
// class "Pynet.cs"
/*/ nuget PyNet\PythonNet; /*/
using Python.Runtime;

/// <summary>
///
Initializes the Python engine, locks its global interpreter and adds scope.
/// Optionally adds Python code; then you can call functions etc.
/// When disposing, unlocks/disposes objects and shuts down the engine.
/// </summary>
public class Pynet : IDisposable {
    Py.GILState _gil;
    PyModule _m;
    bool _shutdown;
    
    /// <summary>
    ///
Initializes the Python engine and optionally adds code.
    /// </summary>
    ///
<param name="code">If not null/"", calls <b>PyModule.Exec</b>.</param>
    ///
<param name="enablePrint">Redirect the output of the Python's print function to console. Default true.</param>
    public Pynet(string code = null, bool enablePrint = true) {
        if (!PythonEngine.IsInitialized) {
            if (Environment.GetEnvironmentVariable("PYTHONNET_PYDLL").NE()) Runtime.PythonDLL = @"C:\Program Files\Python311\python311.dll"; //edit this if need. Or set the environment variable.
            PythonEngine.Initialize();
            _shutdown = true;
            //process.thisProcessExit += _ => PythonEngine.Shutdown(); //does not work
        }
        _gil = Py.GIL();
        
        if (enablePrint) {
            Module.Exec("""
import sys

class NetConsole(object):
    def __init__(self, writeCallback):
        self.writeCallback = writeCallback

    def write(self, message):
        self.writeCallback(message)

    def flush(self):
        pass

def setConsoleOut(writeCallback):
    sys.stdout = NetConsole(writeCallback)
"""
);
            var writer = (string s) => { Console.Write(s); };
            Module.InvokeMethod("setConsoleOut", Python.Runtime.PyObject.FromManagedObject(writer));
        }

        
        if (!code.NE()) Module.Exec(code);
    }

    
    /// <summary>
    ///
Unlocks/disposes Python objects and shuts down the engine.
    /// </summary>
    public void Dispose() {
        _m?.Dispose();
        _gil?.Dispose();
        if (_shutdown) {
            try { PythonEngine.Shutdown(); } //without this the process does not exit
            catch (System.Runtime.Serialization.SerializationException) { } //thrown when using enablePrint
        }
    }

    
    /// <summary>
    ///
Gets the result of <b>Py.CreateScope</b>.
    /// You can assign it to a <c>dynamic</c> variable and call functions defined in your Python code.
    /// </summary>
    public PyModule Module => _m ??= Py.CreateScope();
}


Examples.
 
Code:
Copy      Help
// script "Pynet examples.cs"
/*/ c Pynet.cs; /*/
using Python.Runtime;

#if true

string
code = """
def Multi(a1, a2):
    return a1 * a2

def JoinStr(a1, a2):
    return a1 + a2
"""
;
using var pyn = new Pynet(code);
dynamic m = pyn.Module;
double d1 = m.Multi(2, 3.5);
string s1 = m.JoinStr("joi", "ned");
print.it(d1, s1);

#elif !true

using var pyn = new Pynet();
dynamic mod = Py.Import("math");
print.it(mod.cos(mod.pi * 2));

#elif !true

using var pyn = new Pynet();
print.it(PythonEngine.Eval("3 + 4"));

#elif !true

string code = """
import ctypes
ctypes.windll.user32.MessageBoxW(0, "Text", "Title", 1)
""";
using var pyn = new Pynet();
PythonEngine.RunSimpleString(code);

#endif
#7
Thank you, Gintaras, I'll try it out.
#8
The print statements in Python are not producing output. I've seen in some articles that it's possible to output them in Visual Studio. This can be particularly useful for functions that return void values.


Code:
Copy      Help
string code = """
print("hello world from python!")
""";
using var pyn = new Pynet();
PythonEngine.RunSimpleString(code);
#9
I updated the Pynet code, now it by default enables Python's print.
#10
Thank you, very helpful.


Forum Jump:


Users browsing this thread: 1 Guest(s)