JuliaPy / PyCall.jl

Package to call Python functions from the Julia language
MIT License
1.47k stars 190 forks source link

Support for named tuples #175

Open rofinn opened 9 years ago

rofinn commented 9 years ago

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.

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

stevengj commented 9 years ago

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

rofinn commented 9 years ago

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 commented 9 years ago

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
stevengj commented 9 years ago

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

rofinn commented 9 years ago

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:

stevengj commented 7 years ago

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

tkf commented 5 years ago

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.

baggepinnen commented 3 years ago

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.