Call a python function with named parameter using python.net from a C# code

Question:

I want to call a python function from C# code. To do that, I used Python for .NET to call function as shown in the following lines of code

   using System;
   using Python.Runtime;

   public class Test{
        public static void Main(){
           using(Py.GIL()){
               dynamic lb = Py.Import("lb");
               dynamic result = lb.analyze("SomeValue");

                Console.WriteLine(result);
           }
        }
    }

The python function is something like this:

def analyze(source, printout = False, raw = True):
        # removed for bravity

So the question is, how can I set “raw” to False when I call the analyze function from C# code. I tried the following but it didn’t work.

1. dynamic result = lb.analyze("SomeSource", raw : false); // No result

2. dynamic result = lb.analyze("SomeSource", @"raw = False"); // No result

I know it is easy to do by doing this:

   dynamic result = lb.analyze("SomeSource", False, False);

But what if there is more than six or seven named parameter, it would not be great to insert it all manually and change the one I wanted. For example, the python library that I am using have 12 named parameter with default value including two more parameters with no default value.

UPDATED
I also tried:

3. dynamic result = lb.analyze("SomeSource", raw = false); // C# compilation error 
Asked By: ash

||

Answers:

To apply keyword arguments use:

lb.analyze("SomeSource", Py.kw("raw", false));

See readme.

Another approach is using C# keyword argument syntax that was recently added to pythonnet:

lb.analyze("SomeSource", raw: false);

Answered By: denfromufa

Since I am using one function to call python scripts I have used a Listto hold the parameter values. I am also passing in a class name and function name since my python scripts contain multiple classes with multiple functions. I don’t use the ‘self’ parameter in any of my classes, so they are static functions. I am providing a snippet of my code to help you and anyone else out that is using python in .net. I personally use it for communciation with USB.

Here is an example of one of my callers. Ignore the function name but look at how it’s calls the ExecuteScript and passing int he parameterset. Notice the list is of type object, incase your paramters are a mix of string/int/bool/objects etc.

public string SendCommand(string comport, string command)
    {

        try
        {
            List<object> parameterSet = new() { comport, command };
            string result = _pythonService.ExecuteScript<string>("usb", "usb", "sendCommand", parameterSet);
            return result;
        }
        catch (Exception)
        {
            throw;
        }
    }

Here is a function that executes the class function

         public dynamic? ExecuteScript<T>(string scriptFile, string className, string functionName, List<object> paramset)
    {
        T? result = default;
        try
        {
            // Location of all the python scripts in the project. lets get the python file we are specifying in the function param 
            string file = $"{Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)}\PythonScripts\{scriptFile}.py";

            // This is initalized in the program.cs, but just want to make sure it's initialized in case something happens
            if (!PythonEngine.IsInitialized)
            {
                PythonEngine.Initialize();
                Py.GIL();
            }

            using (var scope = Py.CreateScope())
            {
                PyObject? pythonReturn; // Our returned PythonObject from the python function call
                string code = File.ReadAllText(file); // Get the python file as raw text
                var scriptCompiled = PythonEngine.Compile(code, file); // Compile the code/file
                scope.Execute(scriptCompiled); // Execute the compiled python so we can start calling it.

                PyObject pythonClass = scope.Get(className); // Lets get an instance of the class in python

                // Add parameters to the function?
                if (paramset != null && paramset.Count > 0)
                {

                    PyObject[] pyParams = new PyObject[paramset.Count]; // This is an array of python parameters passed into a function

                    // Loop through our incoming c# list of parameters and create PythonObject array .
                    for (int i = 0; i < paramset.Count; i++)
                    {
                        pyParams[i] = paramset[i].ToPython();
                    }

                    pythonReturn = pythonClass.InvokeMethod(functionName, pyParams); // Call the  function on the class with parameters
                }
                else // We aren't using parameters here
                    pythonReturn = pythonClass.InvokeMethod(functionName); // Call the  function on the class 

                // Lets convert our returned pythonObject to that of the object type (C#)
                object? netObject = pythonReturn.AsManagedObject(typeof(object));

                // A special case of when we want a list back. We will convert the object to the specific type in the caller function
                if (typeof(T) == typeof(IList<object>))
                {
                    object[] something = pythonReturn.As<object[]>();
                    return something;
                }

                // Convert the c# object to that of what we expect to be returned,. string/int/bool/class
                if (netObject != null)
                    result = (T)netObject; // convert the returned string to managed string object 
            }
            return result;
        }
        catch (Exception)
        {
            // Handle your exceptions here
            throw;
        }
    }

If you don’t care about the entire function and just want the quick snippet of adding the params:

// Add parameters to the function?
                if (paramset != null && paramset.Count > 0)
                {

                    PyObject[] pyParams = new PyObject[paramset.Count]; // This is an array of python parameters passed into a function

                    // Loop through our incoming c# list of parameters and create PythonObject array .
                    for (int i = 0; i < paramset.Count; i++)
                    {
                        pyParams[i] = paramset[i].ToPython();
                    }

                    pythonReturn = pythonClass.InvokeMethod(functionName, pyParams); // Call the  function on the class with parameters
                }
                else // We aren't using parameters here
                    pythonReturn = pythonClass.InvokeMethod(functionName); // Call the  function on the class 
Answered By: Exzile