Как использовать метод .NET, который изменяется на месте в Python?

Я пытаюсь использовать .NET dll в Python. В языке .NET метод требует передачи ему 2 массивов по ссылке, которые он затем изменяет:

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

Как я могу использовать этот метод в Python с помощью модуля Python для .NET?

Изменить: забыл упомянуть, что это в CPython, а не в IronPython.

Дополнительная информация. Когда я делаю следующее:

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

Я получаю вывод вроде:

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

Мне просто нужно выяснить, как преобразовать их обратно в типы Python?


person Jonno    schedule 25.10.2013    source источник
comment
Почему вы хотите преобразовать их обратно в типы Python, а не просто использовать их?   -  person abarnert    schedule 30.10.2013
comment
@abarnet Поскольку вы проделали такую ​​большую работу в своем ответе ниже, я собираюсь прокомментировать это.   -  person Jonno    schedule 01.11.2013


Ответы (2)


PythonNet документирует это не так четко, как IronPython, но делает почти то же самое.

Итак, давайте посмотрим на документацию IronPython для ref и out параметров:

Язык Python передает все аргументы по значению. Нет синтаксиса, указывающего, что аргумент должен передаваться по ссылке, как в языках .NET, таких как C# и VB.NET, с помощью ключевых слов ref и out. IronPython поддерживает два способа передачи аргументов ref или out в метод: неявный и явный.

Неявным способом аргумент обычно передается в вызов метода, и его (потенциально) обновленное значение возвращается из вызова метода вместе с обычным возвращаемым значением (если есть). Это хорошо сочетается с возможностью множественных возвращаемых значений Python…

Явным образом вы можете передать экземпляр clr.Reference[T] в качестве аргумента ref или out, и его поле Value будет установлено вызовом. Явный способ полезен, если есть несколько перегрузок с параметрами ref…

Есть примеры и для того, и для другого. Но чтобы адаптировать его к вашему конкретному случаю:

itemIDs, itemNames = GetItems()

Или, если вы действительно хотите:

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

CPython с использованием PythonNet делает в основном то же самое. Самый простой способ сделать out параметры — не передавать их и принимать как дополнительные возвращаемые значения, а для ref параметров передавать входные значения в качестве аргументов и принимать выходные значения в качестве дополнительных возвращаемых значений. Как и неявное решение IronPython. (За исключением того, что функция void с параметрами ref или out всегда возвращает None перед аргументами ref или out, даже если это не так в IronPython.) Вы можете легко понять это, проверив возвращаемые значения. Итак, в вашем случае:

_, itemIDs, itemNames = GetItems()

Между тем, тот факт, что это массивы, не усложняет ситуацию. Как поясняется в документах, PythonNet предоставляет итерируемый интерфейс для всех коллекций IEnumerable, а также протокол последовательности. а также для Array. Итак, вы можете сделать это:

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

И объекты Int32 и String будут преобразованы в собственные объекты int/long и str/unicode так же, как если бы они были возвращены напрямую.


Если вы действительно хотите явно преобразовать их в собственные значения, вы можете это сделать. map или понимание списка даст вам список Python из любого итерируемого объекта, включая оболочку PythonNet вокруг Array или другого IEnumerable. И вы можете явно сделать long или unicode из Int32 или String, если вам нужно. Так:

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

Но я не вижу большого преимущества в этом, если только вам не нужно, например, предварительно проверять все значения перед использованием любого из них.

person abarnert    schedule 25.10.2013
comment
Спасибо, к сожалению, я забыл упомянуть, что использую CPython, а не IronPython. - person Jonno; 26.10.2013
comment
@abernet Я создаю документацию о том, как использовать эту dll в CPython. Я думаю, что будут случаи, когда пользователь захочет использовать встроенные атрибуты объектов python, которые в противном случае были бы недоступны. Оказывается, Python.Net действительно возвращает объекты python, если возвращаемые значения представляют собой строки, целые числа и т. д. Только если возвращаемый объект является массивом, он не преобразуется. Я использовал понимание списка в своей документации в качестве примера того, как преобразовать массив в список (одновременно преобразовывая тип элементов). Спасибо и за идею карты. - person Jonno; 01.11.2013
comment
@abarnet Между прочим, хотя аргументы являются «выходными» параметрами, если я ничего не передаю методу, например: _, itemIDs, itemNames = GetItems(), я получаю следующую ошибку: TypeError: ни один метод не соответствует заданным аргументам - person Jonno; 01.11.2013
comment
@abarnert: Например: r, d = Double.TryParse('12.3') не работает, см. stackoverflow.com/q/54692267/7556646. - person Wollmich; 14.02.2019

Мне удалось использовать метод bool XferData(ref byte[] buf, ref int len) из библиотеки С# CyUSB.dll со следующим кодом:

>>> xferLen = 2;

>>> outData=[10, 0]

>>> inData=[]

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

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

True 10 0 2

Надеюсь, это поможет кому-то.

person Armand    schedule 16.12.2014