Интеграция IronPython в C#: конкретная проблема / вопрос
Я работаю над созданием механизма расширяемости для моего приложения C# mapmaking через IronPython. Все работает нормально, но у меня есть конкретное требование, которое у меня возникают проблемы при реализации: я хочу, чтобы пользователь мог указать две вещи:
- Имя файла скрипта Python для загрузки
- Однострочная строка, содержащая скрипт 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 мс / запуск
Поэтому, я думаю, я попытаюсь использовать свое собственное решение, если только вставка кода не станет проблемой.