Instanciar uma classe python em C #
-
06-09-2019 - |
Pergunta
Eu escrevi uma classe em python que eu quero embrulhar em um .NET assembly via IronPython e instanciar em um aplicativo C #. Eu já migraram da classe de IronPython, criou uma biblioteca de montagem e referenciado-lo. Agora, como faço para realmente obter uma instância dessa classe?
Os olhares de classe (parcialmente) como este:
class PokerCard:
"A card for playing poker, immutable and unique."
def __init__(self, cardName):
O stub de teste que eu escrevi em C # é:
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();
}
}
}
O que eu tenho que fazer, a fim de criar uma instância dessa classe em C #?
Solução
classes IronPython são não .NET classes. Eles são instâncias de IronPython.Runtime.Types.PythonType que é o metaclass Python. Isso ocorre porque as classes Python são dinâmicas e adição de suporte e remoção de métodos em tempo de execução, coisas que você não pode fazer com classes de .NET.
Para usar classes Python em C # você precisará usar a classe ObjectOperations. Esta classe permite operar em tipos de python e instâncias na semântica da própria linguagem. por exemplo. ele usa os métodos mágicos quando apropriado, auto-promove inteiros para longs etc. Você pode descobrir mais sobre ObjectOperations por olhar para a fonte ou usando reflector.
Aqui está um exemplo. Calculator.py contém uma classe simples:
class Calculator(object):
def add(self, a, b):
return a + b
Você pode usá-lo em seu código # 4.0 C .NET pré assim:
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)
Você vai precisar fazer referência as assembléias IronPython.dll, Microsoft.Scripting e Microsoft.Scripting.Core.
C # 4 fez isso muito mais fácil com o new tipo dinâmico.
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);
Se você estiver usando o Visual Studio 2010 ou posterior com NuGet apoio simplesmente executar este para baixar e referenciar as bibliotecas apropriadas.
Install-Package IronPython
Outras dicas
Agora que .NET 4.0 é liberado e tem o tipo dinâmico, este exemplo deve ser atualizado. Usando o mesmo arquivo de python como na resposta original do m-sharp:
class Calculator(object):
def add(self, a, b):
return a + b
Aqui está como você chamaria isso utilizando .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);
Mais uma vez, você precisa adicionar referências a IronPython.dll e Microsoft.Scripting.
Como você pode ver, a configuração inicial e criar o arquivo de origem é a mesma.
Mas uma vez que a fonte seja com sucesso executado, trabalhando com as funções do Python é muito mais fácil graças à nova palavra-chave "dinâmico".
Estou atualizando o exemplo acima fornecida pelo inteligente Humano para as classes IronPython compilados (DLL) em vez de código fonte IronPython em um arquivo .py.
# Compile IronPython calculator class to a dll
clr.CompileModules("calculator.dll", "calculator.py")
C # 4.0 código com o novo tipo de dinâmica é a seguinte:
// 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);
Referências:
Tenho pesquisado alta e baixa e tenho medo que não parece ser muito mais informações relativas a este. Estou praticamente certo que ninguém planejou uma maneira de fazer isso de forma limpa que você gostaria.
A principal razão pela qual eu acho que este é um problema é que, a fim de ver o tipo PokerCard
em seu aplicativo C # você teria que compilar o código Python para IL. Eu não acredito que existam compiladores Python->
IL lá fora.