Instantiating a python class in C#
Asked Answered
W

4

44

I've written a class in python that I want to wrap into a .net assembly via IronPython and instantiate in a C# application. I've migrated the class to IronPython, created a library assembly and referenced it. Now, how do I actually get an instance of that class?

The class looks (partially) like this:

class PokerCard:
    "A card for playing poker, immutable and unique."

    def __init__(self, cardName):

The test stub I wrote in C# is:

using System;

namespace pokerapp
{
    class Program
    {
        static void Main(string[] args)
        {
            var card = new PokerCard(); // I also tried new PokerCard("Ah")
            Console.WriteLine(card.ToString());
            Console.ReadLine();
        }
    }
}

What do I have to do in order to instantiate this class in C#?

Woodley answered 23/2, 2009 at 20:47 Comment(0)
S
56

IronPython classes are not .NET classes. They are instances of IronPython.Runtime.Types.PythonType which is the Python metaclass. This is because Python classes are dynamic and support addition and removal of methods at runtime, things you cannot do with .NET classes.

To use Python classes in C# you will need to use the ObjectOperations class. This class allows you to operate on python types and instances in the semantics of the language itself. e.g. it uses the magic methods when appropriate, auto-promotes integers to longs etc. You can find out more about ObjectOperations by looking at the source or using reflector.

Here is an example. Calculator.py contains a simple class:

class Calculator(object):
    def add(self, a, b):
        return a + b

You can use it from your pre .NET 4.0 C# code like this:

ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();

ObjectOperations op = engine.Operations;

source.Execute(scope); // class object created
object klaz = scope.GetVariable("Calculator"); // get the class object
object instance = op.Call(klaz); // create the instance
object method = op.GetMember(instance, "add"); // get a method
int result = (int)op.Call(method, 4, 5); // call method and get result (9)

You will need to reference the assemblies IronPython.dll, Microsoft.Scripting and Microsoft.Scripting.Core.

C# 4 made this much easier with the new dynamic type.

ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);

dynamic Calculator = scope.GetVariable("Calculator");
dynamic calc = Calculator();
int result = calc.add(4, 5);

If you are using Visual Studio 2010 or later with NuGet support simply execute this to download and reference the appropriate libraries.

Install-Package IronPython
Stelliform answered 23/2, 2009 at 22:14 Comment(5)
I don't have the rep to edit this, but in the Calculator.py sample, the first "def" should be indented. Great example, though! Very helpful. Now that .Net 4.0 is out, it would be nice to see an updated example.Iinden
Added a .Net 4.0 example, using your same Calculator sample class.Iinden
Can you please provide the references necessary for your .net 4 example to work? Update: Sorry, I meant the "using" statements. You have provided the references.Nupercaine
ObjectOperations op doesn't seem to have a .call method in .net 4.5Teak
@SantiPeñate-Vera do you know the correct method to use instead of .call by any chance?Wallie
I
32

Now that .Net 4.0 is released and has the dynamic type, this example should be updated. Using the same python file as in m-sharp's original answer:

class Calculator(object):
    def add(self, a, b):
        return a + b

Here is how you would call it using .Net 4.0:

string scriptPath = "Calculator.py";
ScriptEngine engine = Python.CreateEngine();
engine.SetSearchPaths(new string[] {"Path to your lib's here. EG:", "C:\\Program Files (x86)\\IronPython 2.7.1\\Lib"});
ScriptSource source = engine.CreateScriptSourceFromFile(scriptPath);
ScriptScope scope = engine.CreateScope();
ObjectOperations op = engine.Operations;
source.Execute(scope);

dynamic Calculator = scope.GetVariable("Calculator");
dynamic calc = Calculator();
return calc.add(x,y);          

Again, you need to add references to IronPython.dll and Microsoft.Scripting.

As you can see, the initial setting up and creating of the source file is the same.

But once the source is succesfully executed, working with the python functions is far easier thanks to the new "dynamic" keyword.

Iinden answered 27/4, 2010 at 15:39 Comment(0)
B
0

I am updating the above example provided by Clever Human for compiled IronPython classes (dll) instead of IronPython source code in a .py file.

# Compile IronPython calculator class to a dll
clr.CompileModules("calculator.dll", "calculator.py")

C# 4.0 code with the new dynamic type is as follows:

// IRONPYTHONPATH environment variable is not required. Core ironpython dll paths should be part of operating system path.
ScriptEngine pyEngine = Python.CreateEngine();
Assembly myclass = Assembly.LoadFile(Path.GetFullPath("calculator.dll"));
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("calculator");
dynamic Calculator = pyScope.GetVariable("Calculator");
dynamic calc = Calculator();
int result = calc.add(4, 5);

References:

  1. Using Compiled Python Classes from .NET/CSharp IP 2.6
  2. Static Compilation of IronPython scripts
Berti answered 11/6, 2013 at 14:12 Comment(0)
D
-4

I have searched high and low and I am afraid that there does not seem to be much information pertaining to this. I am pretty much certain that no one has devised a way to do this in the clean manner that you would like.

The main reason I think this is a problem is that in order to see the PokerCard type in your C# application you would have to compile your Python code to IL. I don't believe that there are any Python->IL compilers out there.

Depreciation answered 23/2, 2009 at 21:9 Comment(1)
Perhaps, after 14 years, it's time to review this answer. Especially in view of the other answers here ;)Clarke

© 2022 - 2024 — McMap. All rights reserved.