Replies: 5 comments 9 replies
-
There's nothing to easily achieve this right now. You can do You could write your own |
Beta Was this translation helpful? Give feedback.
-
I started working on this. Would be nice, if someone with deeper knowledge could comment. using PythonCall
const PYTYPES = setdiff(getindex.(getfield.(getfield.(methods(ispy), :sig), :types), 2), [Any])
function pyconvert_native(x)
ispy(x) && PythonCall.pyisinstance(x, pybuiltins.dict) && return pyconvert_dict(x)
types = setdiff(getfield.(PythonCall._pyconvert_get_rules(pytype(x)), :type), PYTYPES)
for T in types
ans = PythonCall.pytryconvert(T, x)
PythonCall.pyconvert_isunconverted(ans) || return PythonCall.pyconvert_result(T, ans)
end
PythonCall.pyconvert_unconverted()
end
function pyconvert_dict(::Type{T}, d) where T
jd = pyconvert(T, d)
kk = pyconvert_native.(keys(jd))
vv = [ispy(v) ? PythonCall.pyisinstance(v, pybuiltins.dict) ? pyconvert_dict(T, v) : pyconvert_native(v) : v for v in values(jd)]
T(zip(kk, vv))
end
pyconvert_dict(d) = pyconvert_dict(Dict, d) which allows us to write the following code snippet julia> pl = pylist([1, 2, 3]);
julia> ps = pystr("H");
julia> pd = @py dict({"a":"bb", "c": dict({"e": "f"})});
julia> pyconvert_native(pl)
3-element Vector{Int64}:
1
2
3
julia> pyconvert_native(ps)
"H"
julia> pyconvert_native(pd)
Dict{String, Any} with 2 entries:
"c" => Dict("e"=>"f")
"a" => "bb"
julia> pyconvert_dict(d)
Dict{String, Any} with 2 entries:
"c" => Dict("e"=>"f")
"a" => "bb"
julia> pyconvert_dict(Dict{String, Any}, d)
Dict{String, Any} with 2 entries:
"c" => Dict{String, Any}("e"=>"f")
"a" => "bb"
julia> pyconvert_dict(Dict{Any, Any}, d)
Dict{Any, Any} with 2 entries:
"c" => Dict{Any, Any}("e"=>"f")
"a" => "bb" |
Beta Was this translation helpful? Give feedback.
-
We can also switch to a similar issue #172 for further discussion. |
Beta Was this translation helpful? Give feedback.
-
I implemented caching, so my current code looks like this: using PythonCall
const PYTYPES = push!(setdiff(getindex.(getfield.(getfield.(methods(ispy), :sig), :types), 2), [Any]), PyArray)
const PYCONVERT_NATIVE_TYPES_CACHE = Dict{PythonCall.C.PyPtr, Vector{Type}}()
function pyconvert_get_native_ruletypes(x::Py)
tptr = PythonCall.C.Py_Type(PythonCall.getptr(x))
get!(PYCONVERT_NATIVE_TYPES_CACHE , tptr) do
pytype = PythonCall.pynew(PythonCall.incref(tptr))
ans = setdiff(getfield.(PythonCall._pyconvert_get_rules(pytype), :type), PYTYPES)
PythonCall.pydel!(pytype)
ans
end
end
function pyconvert_native(x)
ispy(x) || return x
PythonCall.pyisinstance(x, pybuiltins.dict) && return pyconvert_dict(x)
types = pyconvert_get_native_ruletypes(x)
for T in types
ans = PythonCall.pytryconvert(T, x)
PythonCall.pyconvert_isunconverted(ans) || return PythonCall.pyconvert_result(T, ans)
end
PythonCall.pyconvert_unconverted()
end
function pyconvert_dict(::Type{T}, d) where T
jd = pyconvert(T, d)
kk = pyconvert_native.(keys(jd))
vv = [ispy(v) ? PythonCall.pyisinstance(v, pybuiltins.dict) ? pyconvert_dict(T, v) : pyconvert_native(v) : v for v in values(jd)]
T(zip(kk, vv))
end
pyconvert_dict(d) = pyconvert_dict(Dict, d) If this approach looks ok, I would try to cover arrays in a similar manner to dicts. |
Beta Was this translation helpful? Give feedback.
-
With this in place, the following two lines add support for (automatic) DataFrame conversion: # add DataFrame conversion rule
using DataFrames
PythonCall.pyconvert_rule_pandasdataframe(::Type{DataFrame}, x::Py) = DataFrame(PyTable(x))
PythonCall.pyconvert_add_rule("pandas.core.frame:DataFrame", DataFrame, PythonCall.pyconvert_rule_pandasdataframe, PythonCall.PYCONVERT_PRIORITY_NORMAL) so julia> pd_df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns = pylist("ABC"))
Python DataFrame:
A B C
0 1 2 3
1 4 5 6
julia> pyconvert_native(pd_df)
2×3 DataFrame
Row │ A B C
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
2 │ 4 5 6 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I really appreciate how PythonCall.jl avoids automatic conversions, but I was wondering if there might also be a way to enable this conditionally?
The case I have been running into is that a colleague sends me some pickle or npz file with a bunch of nested, heterogeneous Python objects which I would then like to feed into my Julia pipeline. I've been sprinkling
pyconvert
s orPy
objects in multiple places before passing the inputs to various Julia functions to get the expected behavior, but wasn't sure if I was approaching this the right way.I know it would be a hit in performance in the front-end, but is there a way to accomplish this for all python objects that I load (perhaps with auto conversion rules that we specify), or maybe some workflow more in line with this package's philosophy that I am overlooking?
As a basic example, if I have the following pickle:
is there a way to load it in Julia as the following native object?
instead of wrapping with:
So sort of like the PythonCall.jl inverse of
py"<>"o
for returning rawPyObjects
in PyCall.jl?Beta Was this translation helpful? Give feedback.
All reactions