从C#构建Python脚本和调用方法

时间:2022-09-01 16:40:51

Is there any way to make this scenario work?

有没有办法让这个场景有效?

There is a Python script. It is built into a DLL by running this script with IronPython:

有一个Python脚本。它通过使用IronPython运行此脚本而内置到DLL中:

import clr
clr.CompileModules("CompiledScript.dll", "script.py")

The goal is to call this DLL's methods from C# code. .NET Reflector shows there is one class in the DLL - DLRCashedCode and the methods we are interested in are private static methods of this class.

目标是从C#代码调用此DLL的方法。 .NET Reflector显示DLL中有一个类 - DLRCashedCode,我们感兴趣的方法是此类的私有静态方法。

For example, there is a function in the script:

例如,脚本中有一个函数:

def scriptMethod(self, text):
...

Its representation in the DLL is:

它在DLL中的表示是:

private static object scriptMethod(Closure closure1, PythonFunction $function, object self, object text)
{
...
}

Closure and PythonFunction are IronPython classes (from Microsoft.Scripting.dll and IronPython.dll).

Closure和PythonFunction是IronPython类(来自Microsoft.Scripting.dll和IronPython.dll)。

So far so good. Is it possible this method to be called by C# code? The idea of using reflection like

到现在为止还挺好。是否有可能通过C#代码调用此方法?使用反射的想法

Type t = typeof(DLRCachedCode);

string methodName = "scriptMethod";
MethodInfo method = t.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);

object[] parameters = new object[] { "param1", "param2" };  // the "params problem"
method.Invoke(null, parameters);

seems harder because of setting the method's parameters. If they are (any how) initialized correctly, could we expect the method to work smoothly?

因为设置方法的参数似乎更难。如果它们(如何)正确初始化,我们是否可以期望该方法能够顺利运行?

Is there a better way to call this methods from C#? For various different reasons we prefer to have the script built as a .NET assembly and not to call the script itself.

有没有更好的方法从C#调用此方法?出于各种不同的原因,我们希望将脚本构建为.NET程序集,而不是调用脚本本身。

2 个解决方案

#1


8  

Sort of. You cannot access the Python methods directly from C# code. Unless you are playing with C# 4.0 and the dynamic keyword or you are very, very special ;). However, you can compile an IronPython class to a DLL and then use IronPython hosting in C# to access the methods (this is for IronPython 2.6 and .NET 2.0).

有点。您无法直接从C#代码访问Python方法。除非您正在使用C#4.0和动态关键字,否则您非常非常特别;)。但是,您可以将IronPython类编译为DLL,然后在C#中使用IronPython托管来访问这些方法(这适用于IronPython 2.6和.NET 2.0)。

Create a C# program like this:

像这样创建一个C#程序:

using System;
using System.IO;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
// we get access to Action and Func on .Net 2.0 through Microsoft.Scripting.Utils
using Microsoft.Scripting.Utils;


namespace TestCallIronPython
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            ScriptEngine pyEngine = Python.CreateEngine();

            Assembly myclass = Assembly.LoadFile(Path.GetFullPath("MyClass.dll"));
            pyEngine.Runtime.LoadAssembly(myclass);
            ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");

            // Get the Python Class
            object MyClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));

            // Invoke a method of the class
            pyEngine.Operations.InvokeMember(MyClass, "somemethod", new object[0]);

            // create a callable function to 'somemethod'
            Action SomeMethod2 = pyEngine.Operations.GetMember<Action>(MyClass, "somemethod");
            SomeMethod2();

            // create a callable function to 'isodd'
            Func<int, bool> IsOdd = pyEngine.Operations.GetMember<Func<int, bool>>(MyClass, "isodd");
            Console.WriteLine(IsOdd(1).ToString());
            Console.WriteLine(IsOdd(2).ToString());

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

Make a trivial Python class like this:

像这样制作一个简单的Python类:

class MyClass:
    def __init__(self):
        print "I'm in a compiled class (I hope)"

    def somemethod(self):
        print "in some method"

    def isodd(self, n):
        return 1 == n % 2

Compile it (I use SharpDevelop) but the clr.CompileModules method should also work. Then shove the compiled MyClass.dll into the directory where the compiled C# program lives and run it. You should get this as the result:

编译它(我使用SharpDevelop)但clr.CompileModules方法也应该工作。然后将已编译的MyClass.dll推送到编译的C#程序所在的目录并运行它。你应该得到这个结果:

Hello World!
I'm in a compiled class (I hope)
in some method
in some method
True
False
Press any key to continue . . .

This incorporates Jeff's more direct solution that eliminates having to create and compile a small Python 'stub' and also shows how you can create C# function calls that access the methods in the Python class.

这包含了Jeff更直接的解决方案,无需创建和编译小的Python“存根”,还展示了如何创建访问Python类中方法的C#函数调用。

#2


6  

The clr.CompileModules is purely a load-time optimization - it doesn't make the scripts directly available to a static languge like C#. You'll need to host the IronPython runtime, and then you can load the DLL into the runtime and use IronPython's hosting interfaces to access it.

clr.CompileModules纯粹是一个加载时优化 - 它不会使脚本直接可用于像C#这样的静态语言。您需要托管IronPython运行时,然后您可以将DLL加载到运行时并使用IronPython的托管接口来访问它。

#1


8  

Sort of. You cannot access the Python methods directly from C# code. Unless you are playing with C# 4.0 and the dynamic keyword or you are very, very special ;). However, you can compile an IronPython class to a DLL and then use IronPython hosting in C# to access the methods (this is for IronPython 2.6 and .NET 2.0).

有点。您无法直接从C#代码访问Python方法。除非您正在使用C#4.0和动态关键字,否则您非常非常特别;)。但是,您可以将IronPython类编译为DLL,然后在C#中使用IronPython托管来访问这些方法(这适用于IronPython 2.6和.NET 2.0)。

Create a C# program like this:

像这样创建一个C#程序:

using System;
using System.IO;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
// we get access to Action and Func on .Net 2.0 through Microsoft.Scripting.Utils
using Microsoft.Scripting.Utils;


namespace TestCallIronPython
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            ScriptEngine pyEngine = Python.CreateEngine();

            Assembly myclass = Assembly.LoadFile(Path.GetFullPath("MyClass.dll"));
            pyEngine.Runtime.LoadAssembly(myclass);
            ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");

            // Get the Python Class
            object MyClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));

            // Invoke a method of the class
            pyEngine.Operations.InvokeMember(MyClass, "somemethod", new object[0]);

            // create a callable function to 'somemethod'
            Action SomeMethod2 = pyEngine.Operations.GetMember<Action>(MyClass, "somemethod");
            SomeMethod2();

            // create a callable function to 'isodd'
            Func<int, bool> IsOdd = pyEngine.Operations.GetMember<Func<int, bool>>(MyClass, "isodd");
            Console.WriteLine(IsOdd(1).ToString());
            Console.WriteLine(IsOdd(2).ToString());

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

Make a trivial Python class like this:

像这样制作一个简单的Python类:

class MyClass:
    def __init__(self):
        print "I'm in a compiled class (I hope)"

    def somemethod(self):
        print "in some method"

    def isodd(self, n):
        return 1 == n % 2

Compile it (I use SharpDevelop) but the clr.CompileModules method should also work. Then shove the compiled MyClass.dll into the directory where the compiled C# program lives and run it. You should get this as the result:

编译它(我使用SharpDevelop)但clr.CompileModules方法也应该工作。然后将已编译的MyClass.dll推送到编译的C#程序所在的目录并运行它。你应该得到这个结果:

Hello World!
I'm in a compiled class (I hope)
in some method
in some method
True
False
Press any key to continue . . .

This incorporates Jeff's more direct solution that eliminates having to create and compile a small Python 'stub' and also shows how you can create C# function calls that access the methods in the Python class.

这包含了Jeff更直接的解决方案,无需创建和编译小的Python“存根”,还展示了如何创建访问Python类中方法的C#函数调用。

#2


6  

The clr.CompileModules is purely a load-time optimization - it doesn't make the scripts directly available to a static languge like C#. You'll need to host the IronPython runtime, and then you can load the DLL into the runtime and use IronPython's hosting interfaces to access it.

clr.CompileModules纯粹是一个加载时优化 - 它不会使脚本直接可用于像C#这样的静态语言。您需要托管IronPython运行时,然后您可以将DLL加载到运行时并使用IronPython的托管接口来访问它。