pythonnet: Implicit List conversion breaks backwards compatibility + numpy support
Environment
Computer 1:
- Pythonnet version: 2.3, installed via pip I think
- Python version: 2.7.12
- Operating System: Ubuntu 16.04.2
Computer 2:
- Pythonnet version: manual build on master from commit ce14424 (currently 11 commits behind)
- Python version: 2.7.12
- Operating System: Ubuntu 16.04.2
Details
Description: Python calls .NET function which returns a List<MyType>. Python then passes the return value, without modification, to a second .NET function which accepts a List<MyType>. Computer 1 executes this code just fine. On Computer 2, there is no .NET function found that matches the arguments because the return value of the .NET function has been transformed into a Python list.
Python code:
import clr
from MyDotNetProject import PythonInterop
x = PythonInterop.GetDoubleList()
PythonInterop.PrintDoubleList(x)
.NET code:
public class PythonInterop
{
public static List<Double> GetDoubleList() {
var result = new List<Double>();
result.Add(1);
result.Add(2);
result.Add(3);
return result;
}
public static void PrintDoubleList(List<Double> list) {
Console.WriteLine("You list has " + list.Count + " elements");
}
}
The Python code works fine on Computer 1. On Computer 2, the PrintDoubleList call produces TypeError: No method matches given arguments
If I print type(x) in Python, Computer 1 gives me a .NET List type while Computer 2 gives me a Python list. I can print x.Count on Computer 1, but I get a missing attribute error on Computer 2.
If I build manually from the 2.3 tag, I get the same (good) behavior as on Computer 1.
It seems that some feature has been partially added to automatically convert .NET objects into Python objects when possible. I suppose this is ok (though I would prefer that not happen because I don’t want the mandatory performance hit of converting even when I don’t want to convert), but if that’s the intention, there must be automatic conversion of Python objects to .NET objects also. One without the other is a critical bug.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 3
- Comments: 36 (18 by maintainers)
Commits related to this issue
- added RawProxyEncoder Now Python host can force raw encoding for autoconverted .NET types. Enables workaround for #514 — committed to losttech/pythonnet by lostmsu 4 years ago
- added RawProxyEncoder Now Python host can force raw encoding for autoconverted .NET types. Enables workaround for #514 — committed to losttech/pythonnet by lostmsu 4 years ago
- added RawProxyEncoder Now Python host can force raw encoding for autoconverted .NET types. Enables workaround for #514 — committed to losttech/pythonnet by lostmsu 4 years ago
- Add RawProxyEncoder (#1122) Now Python host can force raw encoding for autoconverted .NET types. Enables workaround for #514 — committed to pythonnet/pythonnet by lostmsu 4 years ago
- Add RawProxyEncoder (#1122) Now Python host can force raw encoding for autoconverted .NET types. Enables workaround for #514 — committed to QuantConnect/pythonnet by lostmsu 4 years ago
Hi, I wrote a Python-side Numpy <-> .NET conversion functions that use
ctypes.memmove
based on what @BenjaminPelletier provided above and the 2014 mailing list conversation between David Cook and Jeffrey Bush. It doesn’t require any C# helper code. I think I’ve got support for all the types inSystem
except strings. Performance seems adequate, I can do around 1 MB / ms, after the initial lazy import:The
ctypes.memmove
approach is equally as fast asMarshal.Copy
and doesn’t have any issues with multi-dimensional arrays associated with it. Source code as follows, the__main__
block is all testing code:I don’t see any evidence of memory-leaking.
Edit: one can do a zero-copy with
np.frombuffer
but then you have a mess of memory manged both by Python’s garbage collector and C#'s garbage collector. If people here know how to deal with references in both GC’s let me know.Calling Python from .NET is outside my use case so I’m not familiar with the logistics doing so. But “just like I’d expect” would be for pythonnet to expose explicit .NET types that map to Python types for the user to instantiate (in .NET) by some means before calling Python with them. These types would probably have helper functions for converting/wrapping from common corresponding .NET types.
FWIW, here’s how I’m marshalling arrays between .NET and NumPy, and it seems like it would have been nice to not have to figure out & write this myself:
Python:
.NET:
Yeah, I’m more and more inclined to just deactivate it by default. A runtime switch is possible via the
clr
pseudo-module or an additional function onPython.Runtime
.I have a use-case that is also broken by the #427 change and doesn’t have a good workaround. Consider the following C# class:
Once the List is converted to a python list, any mutations to the python list don’t get updated in the original C# class. You can see this with the following python code:
This breaks any case where you would have some other C# method on the class that needs to use the list after it’s been modified by python.
@denfromufa I also have a numpy array converter here https://github.com/yagweb/pythonnetLab/blob/master/pynetLab/Numpy.cs
With this converter, the example https://github.com/pythonnet/pythonnet#example can be replaced with this one, https://github.com/yagweb/pythonnetLab/blob/master/Test/TestMatplotlib.cs This one may be a better an example, because,
.NET users can create a numpy array with a single line, like this,
var x = Numpy.NewArray(new double[]{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 });
So, No CLR list converting to python list by default is needed.Usage of PyScope and Matplotlib is also included in this example.
@ddsleonardo don’t worry, the default conversion is going to be off by default in the next major version.
I was not here around the time Python.NET started, but I suspect this conversion was added to make most APIs easily callable:
List<T>
does not implement Python collection protocol, and Python’s list does not implementIEnumerable
and other stuff.