# Converting a PyObject to a Julia struct

**URL:** <https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001>\
**Category:** General Usage\
**Created:** [January 7, 2021, 6:58pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001 "2021-01-07T18:58:33Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![chkwon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chkwon/32/3632_2.png) [@chkwon](https://discourse.julialang.org/u/chkwon)\
**Post date:** [January 7, 2021, 6:58pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/1 "2021-01-07T18:58:33Z")

</div>

I’m trying to call Julia from Python vi PyJulia.

In Julia, I have a struct like

```julia
mutable struct Person
  age :: Int
  weight :: Float64 
end

function add_age!(p::Person)
  p.age += 1
end

using PyCall
py_add_age = pyfunction(add_age!, Person)

```

Then, in Python,

```python
p = Main.Person(10, 100.0)
Main.py_add_age(p)

```

This leads to the following conversion error:

```julia
JULIA: MethodError: Cannot `convert` an object of type PyObject to an object of type Person

```

How can I make this conversion possible? If I directly call the Julia function, it works, but just wanted to test the performance improvement via `pyfunction`.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 7, 2021, 7:53pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/2 "2021-01-07T19:53:14Z")

</div>

I wouldn’t use `pyfunction` at all here. If you just do `py_add_age = add_age!`, then the default argument conversion should do what you want.

Otherwise you’d have to define a `convert` function, of the form:

```julia
function Base.convert(::Type{Person}, o::PyObject)
    PyCall.is_pyjlwrap(o) || throw(ArgumentError("not a wrapped Julia object"))
    return PyCall.unsafe_pyjlwrap_to_objref(o)::Person
end

```

---

<div class="post-metadata">

**Author:** ![chkwon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chkwon/32/3632_2.png) [@chkwon](https://discourse.julialang.org/u/chkwon)\
**Post date:** [January 7, 2021, 8:14pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/3 "2021-01-07T20:14:55Z")

</div>

No performance benefit by using `pyfunction`? Could you introduce a use case of `pyfunction` that improves the performance?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 7, 2021, 8:37pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/4 "2021-01-07T20:37:36Z")

</div>

> [@chkwon](#):
>
> No performance benefit by using `pyfunction` ? Could you introduce a use case of `pyfunction` that improves the performance?

Probably a slight benefit to using `pyfunction` and defining a `convert` function since it avoids a type-introspection step, but if you’re worried about the performance of as simple a function as `add_age!` from Python then you’re think about it all wrong — the Python overhead will swamp the cost of the call. `pyfunction` is more useful to customize _which_ conversion is selected in cases where the default is not doing what you want.

To get a performance benefit from Julia in Python, you need to be calling Julia functions that do a lot of work, so that the overhead of calling them from Python is negligible. (It’s exactly analogous to calling a “vectorized” function, written in C or Fortran, from Numpy/Scipy — you “vectorize” so that the function has enough work to amortize the Python overhead.)

---

<div class="post-metadata">

**Author:** ![chkwon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chkwon/32/3632_2.png) [@chkwon](https://discourse.julialang.org/u/chkwon)\
**Post date:** [January 7, 2021, 8:52pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/5 "2021-01-07T20:52:03Z")

</div>

The above example was just a minimal example to illustrate what I wanted to do.

Does it mean that frequently calling of Julia functions from Python could not be so beneficial, depending on the job done in Julia?

The exact context is reinforcement learning, where pytorch trains neural networks where the simulator is done in Julia. Should it be the other way in this case? Julia calling Python, instead of Python calling Julia?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 7, 2021, 9:24pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/6 "2021-01-07T21:24:57Z")

</div>

> [@chkwon](#):
>
> Does it mean that frequently calling of Julia functions from Python could not be so beneficial, depending on the job done in Julia?

You can call Julia as frequently as you want, but to get a performance benefit the Julia function should be doing enough computational work on _each call_ to make the Python-calling overhead negligible.

> The exact context is reinforcement learning, where pytorch trains neural networks where the simulator is done in Julia. Should it be the other way in this case? Julia calling Python, instead of Python calling Julia?

Whether you have Julia calling Python or Python calling Julia, the overhead of Python is there. As long as your simulator is expensive enough, it shouldn’t matter (except for convenience/flexibility … I personally find it easier to work from Julia).

---

<div class="post-metadata">

**Author:** ![chkwon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chkwon/32/3632_2.png) [@chkwon](https://discourse.julialang.org/u/chkwon)\
**Post date:** [January 8, 2021, 1:56am UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/7 "2021-01-08T01:56:43Z")

</div>

Thanks for clarification.

> [@stevengj](#):
>
> Whether you have Julia calling Python or Python calling Julia, the overhead of Python is there.

Where does “the overhead of Python” exactly come from? Handling Julia data in Python, or calling a Julia process from Python, or handling python data in Julia?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 8, 2021, 1:49pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/8 "2021-01-08T13:49:18Z")

</div>

> [@chkwon](#):
>
> Where does “the overhead of Python” exactly come from?

From the same reason why you can’t write fast inner-loop code in Python. Every object that is represented in Python, from an integer to a function, has to be wrapped in a heap-allocated wrapper (a `PyObject`) with a runtime type tag to indicate what type it, and every access to such an object is dynamic — you have to look at the type tag and dynamically dispatch to the correct code to extract the data and perform the operation you want.

---

<div class="post-metadata">

**Author:** ![chkwon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chkwon/32/3632_2.png) [@chkwon](https://discourse.julialang.org/u/chkwon)\
**Post date:** [January 8, 2021, 2:50pm UTC](https://discourse.julialang.org/t/converting-a-pyobject-to-a-julia-struct/53001/9 "2021-01-08T14:50:22Z")

</div>

I very much appreciate your explanation.
