问题
I'm working on providing an extensibility mechanism for my C# mapmaking application through IronPython. Everything works fine, but I have a specific requirement which I'm having trouble implementing: I want the user to be able to specify two things:
- A file name of the Python script to be loaded
- A one-liner string containing Python script which would typically be a call of a function from that Python file (example
getTextLabel(element)
)
These two settings must be separate, but I don't know if it is possible to do this using PythonScript
and related classes.
I'm a newbie in Python, perhaps there is another way to achieve this? For performance reasons I want to avoid loading and compiling the Python script file several times (since there could be several above mentioned different "function call" settings and I want to reuse the CompiledCode
instance for the file if possible).
UPDATE: @digEmAll gave the correct answer to my question, so I'm accepting it as a valid answer. But if you are concerned with performance, you should also check out my own answer.
回答1:
You can do something like this:
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);
As long as you keep the scope
where the module is loaded, you can call functions of the module without reloading it.
回答2:
I've done some testing of @digEmAll's code. First I must say it runs correctly and does what I asked in the question. But I was concerned with the fact that you have to call
string expr = "functionOfMyModule()";
var result = engine.Execute(expr, scope);
every time you want to evaluate a user-defined expression. My concern was that the code is not pre-compiled and has to be reparsed on each execution, which could have a serious effect on the performance of my application (these kinds of expressions could be called hundreds of thousands if not millions of times, so every millisecond counts).
I tried a different solution: simply pasting the user-defined expression at the end of the Python module (I'm not saying this works in all situations!):
def simpleFunc(x):
return x + 2;
# this is where the pasting occurs:
simpleFunc(x)
What I did then is to compile this code:
ScriptSource source = engine.CreateScriptSourceFromString(myCode);
CompiledCode compiledCode = source.Compile();
... create a scope and run it:
ScriptScope scope = engine.CreateScope();
scope.SetVariable ("x", 10);
int result = compiledCode.Execute<int>(scope);
Now I executed both solutions (digEmAll's and my own) on the same piece of code and the same expression 10,000 times and here are the results:
- engine.Execute(expr, scope): 0.29 ms / run
- compiledCode.Execute(scope): 0.01 ms / run
So I guess I'll try to use my own solution, unless the code pasting proves to be a problem.
来源:https://stackoverflow.com/questions/5292366/ironpython-integration-in-c-a-specific-problem-question