如何使用在 Python 中就地修改的 .NET 方法?

Posted

技术标签:

【中文标题】如何使用在 Python 中就地修改的 .NET 方法?【英文标题】:How to use a .NET method which modifies in place in Python? 【发布时间】:2013-11-05 04:33:34 【问题描述】:

我正在尝试在 Python 中使用 .NET dll。在 .NET 语言中,该方法需要通过引用传递 2 个数组,然后对其进行修改:

public void GetItems(
out int[] itemIDs,
out string[] itemNames
)

如何使用 Python for .NET 模块在 Python 中使用此方法?

编辑:忘了说这是在 CPython 而不是 IronPython 中。

附加信息。 当我执行以下操作时:

itemIDs = []
itemNames = []
GetItems(itemIDs, itemNames)

我得到如下输出:

(None, <System.Int32[] at 0x43466c0>, <System.String[] at 0x43461c0>)

我只需要弄清楚如何将这些转换回 python 类型吗?

【问题讨论】:

为什么要将它们转换回 Python 类型而不是仅仅使用它们? @abarnet 既然您在下面的回答中做得如此出色,我将在那里发表评论。 【参考方案1】:

PythonNet 没有像 IronPython 那样清楚地记录这一点,但它几乎做了同样的事情。

那么,让我们看看ref and out parameters 的 IronPython 文档:

Python 语言按值传递所有参数。没有语法表明参数应该通过引用传递,就像在 .NET 语言(如 C# 和 VB.NET)中通过 ref 和 out 关键字。 IronPython 支持将 ref 或 out 参数传递给方法的两种方式,一种是隐式方式,一种是显式方式。

在隐式方式中,参数被正常传递给方法调用,其(可能)更新的值与正常返回值(如果有)一起从方法调用返回。这与 Python 的多返回值特性很好地组合在一起……

以显式方式,您可以为 ref 或 out 参数传递一个 clr.Reference[T] 的实例,其 Value 字段将由调用设置。如果有多个带有 ref 参数的重载,则显式方法很有用……

两者都有例子。但要根据您的具体情况进行调整:

itemIDs, itemNames = GetItems()

或者,如果你真的想要:

itemIDsRef = clr.Reference[Array[int]]()
itemNamesRef = clr.Reference[Array[String]]()
GetItems(itemIDs, itemNames)
itemIDs, itemNames = itemIDsRef.Value, itemNamesRef.Value

使用PythonNet 的CPython 基本上做同样的事情。处理out 参数的简单方法是不传递它们并接受它们作为额外的返回值,并且ref 参数将输入值作为参数传递并接受输出值作为额外的返回值。就像 IronPython 的隐式解决方案一样。 (除了带有refout 参数的void 函数总是在refout 参数之前返回None,即使它不会在IronPython 中。)你可以很容易地弄清楚通过检查返回值。所以,在你的情况下:

_, itemIDs, itemNames = GetItems()

同时,这些恰好是数组这一事实并没有让事情变得更难。正如the docs 解释的那样,PythonNet 为所有IEnumerable 集合提供了可迭代接口,并为Array 提供了序列协议。所以,你可以这样做:

for itemID, itemName in zip(itemIDs, itemNames):
    print itemID, itemName

并且Int32String 对象将被转换为本机int/longstr/unicode 对象,就像它们被直接返回一样。


如果您真的想将这些显式转换为本机值,您可以。 map 或列表推导式将为您提供任何可迭代的 Python 列表,包括围绕 Array 或其他 IEnumerable 的 PythonNet 包装器。如果需要,您可以从Int32String 中显式创建longunicode。所以:

itemIDs = map(int, itemIDs)
itemNames = map(unicode, itemNames)

但我认为这样做并没有多大好处,除非您需要,例如,在使用任何值之前预先检查所有值。

【讨论】:

谢谢,很遗憾我忘了说我使用的是 CPython 而不是 IronPython。 @abernet 我正在创建一些关于如何在 CPython 中使用这个 dll 的文档。我认为在某些情况下,用户会想要使用 python 对象的内置属性,否则这些属性将不可用。事实证明,如果返回是字符串、整数等,Python.Net 确实会返回 python 对象。只有当返回的对象是数组时,它才不会转换。我在我的文档中使用了列表理解作为如何将数组转换为列表的示例(同时也转换了元素的类型)。也感谢您的地图创意。 @abarnet 顺便说一句,即使参数是“输出”参数,如果我没有将任何内容传递给方法,即:_, itemIDs, itemNames = GetItems() 我收到以下错误:TypeError : 没有方法匹配给定的参数 @abarnert:例如:r, d = Double.TryParse('12.3') 不起作用,请参阅***.com/q/54692267/7556646。【参考方案2】:

我已经设法使用该方法 来自 C# 库 CyUSB.dll 的 bool XferData(ref byte[] buf, ref int len) 使用以下代码:

>>> xferLen = 2;

>>> outData=[10, 0]

>>> inData=[]

>>> n, outData, xferLen = XferData(outData, xferLen)

>>> print n, outData[0], outData[1], xferLen

True 10 0 2

希望这对某人有所帮助。

【讨论】:

以上是关于如何使用在 Python 中就地修改的 .NET 方法?的主要内容,如果未能解决你的问题,请参考以下文章

在Python列表中进行就地修改

Python:就地映射[重复]

如何在 Python 中就地对整数数组进行排序?

Ruby方法如何就地修改

使用 jq 就地修改 json 中的键值

删除列表列表中的第一项 - 制作副本而不是就地修改