Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a .NET method which modifies in place in Python?

I am trying to use a .NET dll in Python. In a .NET language the method requires passing it 2 arrays by reference which it then modifies:

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

How can I use this method in Python using the Python for .NET module?

Edit: Forgot to mention this is in CPython not IronPython.

Additional info. When I do the following:

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

I get an output like:

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

Do I just need to figure out how to convert these back into python types?

like image 426
Jonno Avatar asked Oct 25 '13 22:10

Jonno


1 Answers

PythonNet doesn't document this quite as clearly as IronPython, but it does almost the same thing.

So, let's look at the IronPython documentation for ref and out parameters:

The Python language passes all arguments by-value. There is no syntax to indicate that an argument should be passed by-reference like there is in .NET languages like C# and VB.NET via the ref and out keywords. IronPython supports two ways of passing ref or out arguments to a method, an implicit way and an explicit way.

In the implicit way, an argument is passed normally to the method call, and its (potentially) updated value is returned from the method call along with the normal return value (if any). This composes well with the Python feature of multiple return values…

In the explicit way, you can pass an instance of clr.Reference[T] for the ref or out argument, and its Value field will get set by the call. The explicit way is useful if there are multiple overloads with ref parameters…

There are examples for both. But to tailor it to your specific case:

itemIDs, itemNames = GetItems()

Or, if you really want:

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

CPython using PythonNet does basically the same thing. The easy way to do out parameters is to not pass them and accept them as extra return values, and for ref parameters to pass the input values as arguments and accept the output values as extra return values. Just like IronPython's implicit solution. (Except that a void function with ref or out parameters always returns None before the ref or out arguments, even if it wouldn't in IronPython.) You can figure it out pretty easily by inspecting the return values. So, in your case:

_, itemIDs, itemNames = GetItems()

Meanwhile, the fact that these happen to be arrays doesn't make things any harder. As the docs explain, PythonNet provides the iterable interface for all IEnumerable collections, and the sequence protocol as well for Array. So, you can do this:

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

And the Int32 and String objects will be converted to native int/long and str/unicode objects just as if they were returned directly.


If you really want to explicitly convert these to native values, you can. map or a list comprehension will give you a Python list from any iterable, including a PythonNet wrapper around an Array or other IEnumerable. And you can explicitly make a long or unicode out of an Int32 or String if you need to. So:

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

But I don't see much advantage to doing this, unless you need to, e.g., pre-check all the values before using any of them.

like image 138
abarnert Avatar answered Oct 13 '22 12:10

abarnert