非常精通C#和Python(但对.NET 4.x 中的新 dynamic
功能一无所知),我最近决定将IronPython脚本支持添加到我的一个C#应用程序中。我有所有的基本通信工作,例如将Action<>
传入并从Python调用我的C#代码。
但我无法使用C#代码中的python生成器查找文档或示例。我有一个解决方案,但它远非优雅。
我在我的python代码中定义了一个生成器(“test.py”):
def do_yield():
yield 1
yield 2
yield 3
在C#中,我设置了IronPython环境:
// set up IronPython
var ipy = Python.CreateRuntime();
test = ipy.UseFile("test.py");
我已经定义了一个帮助函数来从返回的PythonGenerator
转换为C#迭代器,所以我的foreach
看起来像:
foreach (int n in PythonGeneratorToCSharpIterator<int>(test.do_yield()))
{
Log("py gen returned: " + n);
}
我的助手功能:
IEnumerable<T> PythonGeneratorToCSharpIterator<T>(IronPython.Runtime.PythonGenerator pyGenerator)
{
while (true) {
T t = default(T);
try {
// get the next value
object o = pyGenerator.next();
// will throw an exception if it's not the correct type (or no more values in the generator)
t = (T)o;
}
catch (Exception ex) {
break; // break out of the while loop and return from the iterator
}
yield return t; // this can't be inside try/catch
}
}
当没有更多值要返回时,python生成器返回LightException
,因此行t = (T)o
将抛出异常,因为它试图将其强制转换为int
。
这样做的一个问题是我没有抓住并正确处理python代码中的任何异常。我只是扔掉它们并退出循环。另一个是当我没有生成器的值时,我抛出一个异常,我更喜欢布尔检查来测试返回的值是无效的。
也许这是我对.NET的新dynamic
方面的无知,这使我无法理解如何正确编码。是否有更好/更标准的方法在C#/ IronPython中编写代码?
修改
从vcsjones
'评论,我现在使用Linq的Cast
扩展方法,这个代码对我很有用:
var gen = (IronPython.Runtime.PythonGenerator)test.do_yield();
foreach(int n in gen.Cast<int>()) {
Log("py gen returned (with Cast): " + n);
}
答案 0 :(得分:1)
这很容易使用dynamic
:
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("test.py");
foreach (int n in test.do_yield())
{
Console.WriteLine("py gen returned: " + n);
}
// outputs
py gen returned: 1
py gen returned: 2
py gen returned: 3
你应该注意的一件大事是,扩展方法(如LINQ)不适用于dynamic
,因为扩展方法只是静态方法调用,并且应用了一些语法糖。而object in this case实施IEnumerable
但未IEnumerable<int>
,限制了您如何处理此问题的选项。
假设您要将项目过滤为偶数项目。这是一种方法:
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("test.py");
foreach (int n in test.do_yield())
{
if (n % 2 == 0)
Console.WriteLine("py gen returned: " + n);
}
或者你可以强制转换然后使用LINQ扩展方法(这是有效的,因为IEnumerable
强制转换允许在编译时评估其余的代码,就像非动态代码一样):
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("test.py");
foreach (int n in ((IEnumerable)test.do_yield()).Cast<int>().Where(n => n % 2 == 0))
{
Console.WriteLine("py gen returned: " + n);
}
在某些情况下,您可能希望使用不带语法糖的扩展方法,例如
// these lines are generally equivalent, unless someList is dynamic
var filteredList = Enumerable.Where(someList, x => x % 2 == 0);
var filteredList = someList.Where(x => x % 2 == 0);