PyCall.jl
PyCall.jl copied to clipboard
Support for named tuples
Currently the behaviour is to just convert named tuples in python to julia tuples, which obviously doesn't work if you want to pass that tuple to another python function that is expecting a named tuple.
If you want to leave it as a Python object, you can do so by using pycall. i.e. pycall(function_returning_named_tuple, PyObect, args....)
Julia doesn't have named tuples, so the alternatives here are
- Current behavior: Default to converting to a Julia tuple, require an explicit
PyObjectif you want to keep it as a Python named tuple. - Default to returning a
PyObjectfor named tuples, require an explicitconvertif you want a Julia tuple. (However, sincePyObjectis iterable, you can maybe still use it like a tuple in some ways in Julia, even without callingconvert.) - Convert to some other Julia type, e.g. a
Vector{Pair{Symbol,Any}}.
So I'm kind of in favour of the third option where the julia type is a wrapper around the Vector{Pair{Symbol, Any}} called something like PyNamedTuple. For now I'll just try using pycall though.
So it turns out that if you use namedtuple = pycall(PyObject(my_python_func), PyObject, args...) the resulting PyObject still doesn't seem to store the names/keys, so passing that PyObject to another python function that expects a namedtuple will still result in an AttributeError on the keys/names and python still seems to think that the PyObject is just a tuple and not a namedtuple.
Ex)
julia> task = pycall(PyObject(arbiter.create_task), PyObject, length, "foo")
PyObject (<PyCall.jlwrap length>, 'foo', frozenset(), 0, datetime.timedelta(0), True, datetime.datetime(2015, 7, 30, 17, 16, 33, 654000))
julia> s[:add_task](task)
ERROR: PyError (:PyObject_Call) <class 'AttributeError'>
AttributeError("'tuple' object has no attribute 'name'",)
File "/Users/rory/repos/Arbiter/.tox/py34/lib/python3.4/site-packages/arbiter/scheduler.py", line 102, in add_task
if not Graph.valid_name(task.name):
in pyerr_check at /Users/rory/.julia/v0.4/PyCall/src/PyCall.jl:58
in pycall at /Users/rory/.julia/v0.4/PyCall/src/PyCall.jl:91
in fn at /Users/rory/.julia/v0.4/PyCall/src/conversions.jl:188
Rory, you need to get the PyObject of my_python_func to start with. If you get the Julia wrapper, function, then convert it back to a PyObject, it won't work because the Julia wrapper function performs the conversion for you. (This will go away once I pull the trigger on #101.)
For example, if you want the method foo in module M, to get the raw PyObject of foo you can do pyimport("M")["foo"].
Awesome, thanks! Sorry, I didn't put two and two together with your pycall and pyimport documentation. Also, I really look forward to the #101 change :+1:
Now that Julia 0.7 has a NamedTuple type (#22194), it should be possible to fully support bidirectional conversion to/from Python named tuples.
I'm actually opposed to automatically converting Python's named tuple to Julia's. This is because Python's named tuples use nominal type whereas Julia's named tuples use structural type. It means that it is impossible to get the right named tuple from Python-Julia-Python round trip.
I think it would be much better to default to returning a PyObject rather than converting the python namedtuple into a julia tuple. The python code
nt = fun1()
fun2(nt)
can not be replaced with the julia version
nt = py.fun1()
py.fun2(nt)
since the named tuple nt has been butchered by the automatic conversion.
The automatic conversion appears to be destroying my object even if I do
some_and_nt = pycall(ts.load_params, PyObject, args...)
where some_and_nt is a tuple of something and a named tuple in python. Here, I declare that I want to get a PyObject back, but when I acces some_and_nt[2] to pass the namedtuple into another function, it has been converted to a julia tuple and the call fails.