# PyCall not respecting class inheritance?

**URL:** <https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840>\
**Category:** General Usage\
**Created:** [August 15, 2023, 7:28pm UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840 "2023-08-15T19:28:53Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![hatmatrix](https://avatars.discourse-cdn.com/v4/letter/h/5f8ce5/32.png) [@hatmatrix](https://discourse.julialang.org/u/hatmatrix)\
**Post date:** [August 15, 2023, 7:28pm UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/1 "2023-08-15T19:28:53Z")

</div>

I have a Python package that I’m importing but class inheritance information is lost, so child class methods are not accessible. I can reproduce this problem with this simpler example here that uses PyCall but does not involve importing Python packages:

```julia
using PyCall

py"""
def defineDict():
    d = MyDict()
    d["a"] = 1
    d["b"] = 2
    return d

class MyDict(dict):
    def get_b(self):
        return self["b"]
"""

const defineDict = py"defineDict"
mydict = defineDict()

```

Then I get

```julia
julia> typeof(mydict)
Dict{Any, Any}

```

which is a Julia class converted from the Python parent class, so does not have the required method.

```julia
julia> mydict.get_b()
ERROR: type Dict has no field get_b

```

Even if I try to force the type to a PyObject, I still don’t inherit the methods.

```julia
julia> mydict2::PyObject = defineDict()
julia> typeof(mydict2)
PyObject
julia> mydict2.get_b()
ERROR: KeyError: key :get_b not found

```

I can confirm the method does not exist for this PyObject with `PyCall.inspect[:getmembers](mydict2)`.

I _can_ get it to work if I import the class method as a separate function.

```julia
const get_b = py"MyDict.get_b"
julia> get_b(mydict)
2

```

Is this the best way to access this method or it possible to force PyCall to respect class inheritance?

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [August 16, 2023, 9:53am UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/2 "2023-08-16T09:53:46Z")

</div>

This is a conversion issue, not an inheritance one: you get the conversion to Julia `Dict` (and we lose the `.` member access).

The solution is to retrieve a _real_ Python `MyDict` object (please note that I used `py"..."o` (that `o` at the end is doing the trick):

```julia
using PyCall

py"""
class MyDict(dict):
    def get_b(self):
        return self["b"]

def defineDict():
    d = MyDict()
    d["a"] = 1
    d["b"] = 2
    return d
"""

defineDict() = py"defineDict()"o
mydict = defineDict()
mydict.get_b() # 2

```

You attempt at `mydict2::PyObject = defineDict()` doesn’t work because when calling your instance of `defineDict` you already return a Julia `Dict`, and you only wrap it back to `PyObject` (so at that point the `member` information is already lost).

Another way to prevent the conversion to Julia `Dict` (and retain the Python object) is the following:

```julia
using PyCall

py"""
class MyDict(dict):
    def get_b(self):
        return self["b"]

def defineDict():
    d = MyDict()
    d["a"] = 1
    d["b"] = 2
    return d
"""
defineDict2() = pycall(py"defineDict", PyObject)
mydict2 = defineDict2()
mydict2.get_b() # 2

```

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [August 16, 2023, 10:33am UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/3 "2023-08-16T10:33:05Z")

</div>

I have heard that PythonCall.jl  
(which is as i understand it, more or less a more modern replacement for PyCall.jl)  
does not have this issue.  
Because it is much less aggressive than PyCall with converting things to julia types.

---

<div class="post-metadata">

**Author:** ![hatmatrix](https://avatars.discourse-cdn.com/v4/letter/h/5f8ce5/32.png) [@hatmatrix](https://discourse.julialang.org/u/hatmatrix)\
**Post date:** [August 16, 2023, 11:14am UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/4 "2023-08-16T11:14:37Z")

</div>

Interesting, thanks for the explanation. If the function and class definition were contained in a python package called `module`, can the code above be adapted? For instance

```julia
@pyimport import module
defineDict = pycall(module.defineDict, PyObject)

```

Does not work as it throws an error - not sure this is valid.

---

<div class="post-metadata">

**Author:** ![hatmatrix](https://avatars.discourse-cdn.com/v4/letter/h/5f8ce5/32.png) [@hatmatrix](https://discourse.julialang.org/u/hatmatrix)\
**Post date:** [August 16, 2023, 11:15am UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/5 "2023-08-16T11:15:21Z")

</div>

Oh, I thought PyCall succeeded PythonCall - I had it the other way around.

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [August 16, 2023, 12:07pm UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/6 "2023-08-16T12:07:16Z")

</div>

Yes, that should work. Here is an example using the `math` module:

```julia
using PyCall

function f()
    math = pyimport("math")
    pycall(math.sin, PyObject, 2.0)
end

f() # PyObject 0.90929742...

```

---

<div class="post-metadata">

**Author:** ![hatmatrix](https://avatars.discourse-cdn.com/v4/letter/h/5f8ce5/32.png) [@hatmatrix](https://discourse.julialang.org/u/hatmatrix)\
**Post date:** [August 16, 2023, 1:42pm UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/7 "2023-08-16T13:42:26Z")

</div>

I see, you have to call through `pycall`. Thanks a ton.

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [August 16, 2023, 2:38pm UTC](https://discourse.julialang.org/t/pycall-not-respecting-class-inheritance/102840/8 "2023-08-16T14:38:51Z")

</div>

It depends - `pycall` offers good control over the return type: but if the regular `py"..."` usage already returns the type you want (and in many cases it does), then `pycall` can be unnecessarily complex.

However, for your use case, `pycall` is the best approach to prevent the conversion to Julia’s `Dict` and retain the Python object (although you can also see that the same can be achieved by `py"..."o` approach).
