PyCall.jl icon indicating copy to clipboard operation
PyCall.jl copied to clipboard

Support for named tuples

Open rofinn opened this issue 10 years ago • 9 comments

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.

rofinn avatar Jul 29 '15 18:07 rofinn

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....)

stevengj avatar Jul 30 '15 18:07 stevengj

Julia doesn't have named tuples, so the alternatives here are

  • Current behavior: Default to converting to a Julia tuple, require an explicit PyObject if you want to keep it as a Python named tuple.
  • Default to returning a 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.)
  • Convert to some other Julia type, e.g. a Vector{Pair{Symbol,Any}}.

stevengj avatar Jul 30 '15 18:07 stevengj

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.

rofinn avatar Jul 30 '15 19:07 rofinn

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

rofinn avatar Jul 30 '15 22:07 rofinn

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"].

stevengj avatar Jul 30 '15 22:07 stevengj

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:

rofinn avatar Jul 31 '15 00:07 rofinn

Now that Julia 0.7 has a NamedTuple type (#22194), it should be possible to fully support bidirectional conversion to/from Python named tuples.

stevengj avatar Nov 02 '17 19:11 stevengj

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.

tkf avatar Mar 15 '19 15:03 tkf

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.

baggepinnen avatar Mar 08 '21 15:03 baggepinnen