Интеграция IronPython в C#: конкретная проблема/вопрос

Я работаю над обеспечением механизма расширения для своего приложения для создания карт C# через IronPython. Все работает нормально, но у меня есть конкретное требование, которое я не могу реализовать: я хочу, чтобы пользователь мог указать две вещи:

  1. Имя файла скрипта Python для загрузки
  2. Однострочная строка, содержащая скрипт Python, который обычно представляет собой вызов функции из этого файла Python (пример getTextLabel(element)).

Эти две настройки должны быть отдельными, но я не знаю, можно ли это сделать с помощью PythonScript и связанных с ними классов.

Я новичок в Python, возможно, есть другой способ добиться этого? Из соображений производительности я хочу избежать загрузки и компиляции файла скрипта Python несколько раз (поскольку может быть несколько вышеупомянутых различных настроек «вызова функции», и я хочу повторно использовать экземпляр CompiledCode для файла, если это возможно).

ОБНОВЛЕНИЕ: @digEmAll дал правильный ответ на мой вопрос, поэтому я принимаю его как правильный ответ. Но если вас беспокоит производительность, вам также следует проверить мой собственный ответ.


person Igor Brejc    schedule 13.03.2011    source источник


Ответы (2)


Вы можете сделать что-то вроде этого:

string importScript = "import sys" + Environment.NewLine +
                      "sys.path.append( r\"{0}\" )" + Environment.NewLine +
                      "from {1} import *";

// python script to load
string fullPath = @"c:\path\mymodule.py";

var engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();

// import the module
string scriptStr = string.Format(importScript,
                                 Path.GetDirectoryName(fullPath),
                                 Path.GetFileNameWithoutExtension(fullPath));
var importSrc = engine.CreateScriptSourceFromString(scriptStr,Microsoft.Scripting.SourceCodeKind.File);
importSrc.Execute(scope);

// now you ca execute one-line expressions on the scope e.g.
string expr = "functionOfMyModule()";
var result = engine.Execute(expr, scope);

Пока вы сохраняете scope, где загружается модуль, вы можете вызывать функции модуля, не перезагружая его.

person digEmAll    schedule 13.03.2011
comment
Спасибо, я проведу некоторое тестирование, и если это сработает, я приму ваш ответ. Кстати, есть ли способ клонировать прицел? Например, чтобы один вызов функции не влиял на выполнение последующих вызовов функций? - person Igor Brejc; 14.03.2011
comment
Ммм, я не думаю, что это возможно... а если бы и было, то это было бы ненамного быстрее, чем создать новый прицел и перезагрузить нужные модули... - person digEmAll; 14.03.2011
comment
ваш код работает, спасибо за вашу помощь. Также проверьте мой собственный ответ. - person Igor Brejc; 14.03.2011

Я протестировал код @digEmAll. Во-первых, я должен сказать, что он работает правильно и делает то, что я задал в вопросе. Но я был обеспокоен тем, что вы должны позвонить

string expr = "functionOfMyModule()";
var result = engine.Execute(expr, scope);

каждый раз, когда вы хотите оценить пользовательское выражение. Меня беспокоило то, что код не был предварительно скомпилирован и должен повторно анализироваться при каждом выполнении, что может серьезно повлиять на производительность моего приложения (эти виды выражений могут вызываться сотни тысяч, если не миллионы раз, поэтому каждая миллисекунда на счету).

Я попробовал другое решение: просто вставить пользовательское выражение в конец модуля Python (я не говорю, что это работает во всех ситуациях!):

def simpleFunc(x):
    return x + 2;

# this is where the pasting occurs:
simpleFunc(x)

Что я сделал тогда, так это скомпилировал этот код:

 ScriptSource source = engine.CreateScriptSourceFromString(myCode);
 CompiledCode compiledCode = source.Compile();

... создайте область и запустите ее:

 ScriptScope scope = engine.CreateScope();
 scope.SetVariable ("x", 10);
 int result = compiledCode.Execute<int>(scope);

Теперь я выполнил оба решения (digEmAll и свое собственное) для одного и того же фрагмента кода и одного и того же выражения 10 000 раз, и вот результаты:

  • engine.Execute(expr, scope): 0,29 мс/запуск
  • скомпилированныйCode.Execute(область): 0,01 мс/запуск

Поэтому я думаю, что попытаюсь использовать свое собственное решение, если вставка кода не окажется проблемой.

person Igor Brejc    schedule 14.03.2011
comment
Да, это тоже отлично работает. Конечно, скомпилировать код (и сохранить вызов Execute) быстрее, я об этом не подумал ;) - person digEmAll; 14.03.2011
comment
Ребята, а что, если на стороне C# вы не знаете имен параметров/переменных и хотите заменить параметры реальными значениями, например A if B > C else D, то есть данное выражение, как бы вы заменили такие параметры A,B,C,D реальными числами? У меня есть только такая строка выражения. заранее спасибо - person sll; 17.03.2012