# Overloading getproperty(::PyObject) - Plotting Measurements.jl errorbars with PyPlot.jl

**URL:** https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382
**Category:** General Usage
**Tags:** plotting, pyplot, measurements
**Created:** [June 22, 2021, 4:27pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382 "2021-06-22T16:27:30Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [June 22, 2021, 4:27pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/1 "2021-06-22T16:27:30Z")

</div>

I want to automatically plot arrays of `Measurement`’s as scatterplots with errorbars using `PyPlot.jl`. For the interactive PyPlot interface adding the following method to `scatter()` works nicely:

```julia
using PyPlot
import PyPlot: scatter
import Measurements: value, uncertainty

function scatter(x::AbstractArray{<:Measurement}, y::AbstractArray{<:Measurement}, args...; kwargs...)
    errorbar(value.(x), value.(y), uncertainty.(y), uncertainty.(x), args...; kwargs...)
end

scatter(x, y::AbstractArray{<:Measurement}, args...; kwargs...) = scatter(x .± 0, y, args...; kwargs...)
scatter(x::AbstractArray{<:Measurement}, y, args...; kwargs...) = scatter(x, y .± 0, args...; kwargs...)

# Test the new scatter
x = 1:0.1:3
y = sin.(x) .± 0.2cos.(x)
scatter(x, y, marker="s", color="C2")

```

However, I don’t know how to preceed to get a similar effect with the object-oriented interface of matplotlib. I.e. I want to be able to do

```julia
fig, ax = PyPlot.subplots(figsize=(6,4))
x = 1:0.1:3
y = sin.(x) .± 0.2cos.(x)
ax.scatter(x, y, marker="s", color="C2")

```

and get the same plot as before.

I guess I would have to somehow overload `getproperty(::PyObject, ::Symbol)` to get a different method returned for `getproperty(ax, :plot)`. But since `ax` is of type `PyObject`, I can’t really dispatch on that without affecting _all_ other `PyObjects` as well. Also, is it possible to add methods to a `PyObject <bound method Axes.plot of <AxesSubplot:>>`, or is that all unchangeable from 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: [June 22, 2021, 6:26pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/2 "2021-06-22T18:26:44Z")

</div>

This is possible, but realize that you would have to [monkey-patch](https://stackoverflow.com/questions/5626193/what-is-monkey-patching) Matplotlib itself (which is possible via PyCall, but not generally recommended)…

---

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [June 22, 2021, 8:36pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/3 "2021-06-22T20:36:03Z")

</div>

Sounds like it is time to dive into the depths of PyCall (or decide I don’t really need the feature that badly) 😃

---

<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: [June 23, 2021, 12:12pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/4 "2021-06-23T12:12:47Z")

</div>

> [@jlbosse](#):
>
> Sounds like it is time to dive into the depths of PyCall

It’s not really the depths — you do it pretty much the same way you would do it in Python. To change the `Axes.scatter` method, I think you can do something like

```julia
let oldscatter = matplotlib.axes.Axes.scatter
    myscatter(args...) = oldscatter(args...)
    function myscatter(self, x::AbstractArray{<:Measurement}, ...)
        # do something 
    end
    matplotlib.axes.Axes.scatter = myscatter # monkey-patch the Axes class
end

```

where you should remember that class methods take `self` as a first argument.

---

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [June 23, 2021, 1:10pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/5 "2021-06-23T13:10:51Z")

</div>

It seems there is no `self` argument when calling `ax.scatter`: If I do as you suggest

```julia
using PyPlot
using Measurements
import Measurements: value, uncertainty

let oldscatter = matplotlib.axes.Axes.scatter
    function myscatter(args...; kwargs...)
        @show args
        @show kwargs
        oldscatter(args...; kwargs...)
    end
    function myscatter(self,
                       x::AbstractArray{<:Measurement},
                       y::AbstractArray{<:Measurement},
                       args...; kwargs...)
        self.errorbar(value.(x), value.(y), uncertainty.(y), uncertainty.(x), args...; kwargs...)
    end
    matplotlib.axes.Axes.scatter = myscatter
end

```

and then test via

```julia
fig, ax = PyPlot.subplots(figsize=(6,4))
x = 1:0.1:3
y = sin.(x) .± 0.2cos.(x)
ax.scatter(x.± 0, y, marker="s", color="C2")

```

I get the following errors

```julia
args = (Measurement{Float64}[1.0 ± 0.0, 1.1 ± 0.0, 1.2 ± 0.0, 1.3 ± 0.0, 1.4 ± 0.0, 1.5 ± 0.0, 1.6 ± 0.0, 1.7 ± 0.0, 1.8 ± 0.0, 1.9 ± 0.0, 2.0 ± 0.0, 2.1 ± 0.0, 2.2 ± 0.0, 2.3 ± 0.0, 2.4 ± 0.0, 2.5 ± 0.0, 2.6 ± 0.0, 2.7 ± 0.0, 2.8 ± 0.0, 2.9 ± 0.0, 3.0 ± 0.0], Measurement{Float64}[0.84 ± 0.11, 0.891 ± 0.091, 0.932 ± 0.072, 0.964 ± 0.053, 0.985 ± 0.034, 0.997 ± 0.014, 0.9996 ± -0.0058, 0.992 ± -0.026, 0.974 ± -0.045, 0.946 ± -0.065, 0.909 ± -0.083, 0.86 ± -0.1, 0.81 ± -0.12, 0.75 ± -0.13, 0.68 ± -0.15, 0.6 ± -0.16, 0.52 ± -0.17, 0.43 ± -0.18, 0.33 ± -0.19, 0.24 ± -0.19, 0.14 ± -0.2])
kwargs = Base.Iterators.Pairs(:color => "C1")
ERROR: MethodError: no method matching Float64(::Measurement{Float64})
Closest candidates are:
  (::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat at rounding.jl:200
  (::Type{T})(::T) where T<:Number at boot.jl:760
  (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number} at char.jl:50
  ...

```

indicating that the first version of `myscatter` is called and without a `self` argument.

Where is `self` here? I could of course change to `myscatter(x::AbstractArray{<:Measurement}, y::...)` to get correctly dispatched on, but then I don’t know how to get `self` to draw to the correct axis.

---

<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: [June 23, 2021, 1:55pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/6 "2021-06-23T13:55:59Z")

</div>

I wonder if Python is getting confused because `myscatter` gets converted to a callable Python object, but not a “real” Python function? Maybe try

```julia
matplotlib.axes.Axes.scatter = PyCall.jlfun2pyfun(myscatter)

```

which wraps it in a Python `lambda`.

---

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [June 23, 2021, 2:37pm UTC](https://discourse.julialang.org/t/overloading-getproperty-pyobject-plotting-measurements-jl-errorbars-with-pyplot-jl/63382/7 "2021-06-23T14:37:10Z")

</div>

Nope, now it doesn’t even call `myscatter` but uses the old `scatter` method (The `args = ...` output doesn’t show up and I get the same error message)
