imagej / pyimagej

Use ImageJ from Python
https://pyimagej.readthedocs.io/
Other
472 stars 82 forks source link

ij.py.from_java() returns scyjava.JavaList when input is java.util.ArrayList #100

Closed carshadi closed 3 years ago

carshadi commented 3 years ago
import imagej
ij = imagej.init(fiji_path, headless=False)
from scyjava import jimport
ArrayList = jimport('java.util.ArrayList')
arrayList = ArrayList()
print(type(arrayList))  # <java class 'java.util.ArrayList'>
print(type(ij.py.from_java(arrayList)))  # <class 'scyjava.JavaList'>

I'm not sure if this is the intended behavior, but the Usage.md states the return type should be a Python list

Running pyimagej 1.0.0 installed via conda-forge with openjdk 8.0.265 in a jupyter notebook

ctrueden commented 3 years ago

@carshadi Thanks for the report. It's confusing because when converting collections from Python to Java, data is copied. But when converting collections from Java to Python, the Java objects are wrapped using Python duck typing. So the type scyjava.JavaList is a Python class wrapping some Java object implementing java.util.List. Technically, the scyjava.JavaList object isn't a Python list, but it has all the same attributes and functions, which means that from a Pythonic perspective, it can be used as a list without issue (duck typing!).

That said, I believe there are a couple of gaps in the implemented functions of the Python collection wrappers. Not sure about JavaList, but I remember running into an issue recently with JavaSet, where some set functions from newer versions of Python were missing from the wrapper.

Now that we're using JPype, it's often the case that you don't need the scyjava.to_python a.k.a. ij.py.from_java function at all anymore, because JPype has a lot more magic than PyJNIus does. E.g.:

>>> import scyjava
>>> def process_list(l):
...   for i in l:
...     print(i + i)
... 
>>> process_list([1, 2, 3, 4])
2
4
6
8
>>> ArrayList = scyjava.jimport('java.util.ArrayList')
>>> jl = ArrayList([1, 2, 3, 4])
>>> process_list(jl)
2
4
6
8
>>> jl = ArrayList(['a', 'b', 'c', 'd'])
>>> process_list(jl)
aa
bb
cc
dd
>>> process_list(['a', 'b', 'c', 'd'])
aa
bb
cc
dd
>>> jl + jl
<java object 'java.util.ArrayList'>
>>> print(jl + jl)
[a, b, c, d, a, b, c, d]
>>> set(jl) - set(['b', 'd'])
{'a', 'c'}

If you really need a true list, you can do:

>>> list(jl)
['a', 'b', 'c', 'd']

So hopefully that's good enough for now!

Still, let us know if there are specific conversions you need that aren't working.

carshadi commented 3 years ago

thanks, this is helpful. casting with list() is just fine for us