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

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

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

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

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

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

Ответы [ 2 ]

5 голосов
/ 14 марта 2011

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

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

4 голосов
/ 14 марта 2011

Я провел некоторое тестирование @ digEmAll's кода.Сначала я должен сказать, что он работает правильно и делает то, что я спросил в вопросеНо меня беспокоил тот факт, что вам нужно вызывать

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 мс / прогон
  • compiledCode.Execute (scope): 0,01 мс / прогон

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

...