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

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

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

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

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

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

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 где модуль загружен, вы можете вызывать функции модуля без перезагрузки.

Я провел некоторое тестирование кода @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 и мое собственное) для одного и того же куска кода и одного и того же выражения 10000 раз, и вот результаты:

  • engine.Execute (expr, scope): 0,29 мс / прогон
  • compiledCode.Execute(scope): 0,01 мс / запуск

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

Другие вопросы по тегам