Open rofinn opened 9 years ago
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
PyObject
if you want to keep it as a Python named tuple.PyObject
for named tuples, require an explicit convert
if you want a Julia tuple. (However, since PyObject
is iterable, you can maybe still use it like a tuple in some ways in Julia, even without calling convert
.)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.
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.