0

I am trying to write a simple Python matplotlib plotting Class that will set up formatting of the plot as I would like. To do this, I am using a class and a subclass. The parent generates an axis and figure, then sets all axis properties that I would like. Then the child generates the plot itself - scatter plot, line, etc. - and saves the plot to an output file.

Here is what I have come up with for the purposes of this question (the plot itself is based on this code):

import matplotlib.pyplot as plt
import numpy as np

class myplot():

    def __init__(self,dims,padd):
        self.fig = plt.figure(figsize=(8.5, 11))
        self.ax = self.fig.add_axes(dims)
        self.pad = padd

    def ax_prop(self):
        self.ax.set_axisbelow(True)
        self.ax.tick_params(axis='x', pad=self.pad, which='both')

class plotter(myplot):
    def __init__(self,dims,padd,x,y):
        myplot.__init__(self,dims,padd)
        self.x = x
        self.y = y

    def mk_p(self):
        self.ax.plot(self.x, self.y, linestyle = '-')
        plt.savefig('Outfile.png', facecolor=self.fig.get_facecolor(), dpi = 300)
        plt.show()

if __name__ == "__main__":
    x = np.arange(0.0, 2.0, 0.01)
    y = np.sin(2*np.pi*x)
    propr = [0.60, 0.2, 0.2, 0.25]; padding =5 
    plt_instance = myplot(propr,padding)
    plt_instance.ax_prop()
    pl_d = plotter(propr,padding,x,y)
    pl_d.mk_p()

I'm tying to base this on the following:

The child must have all the properties of the parent.

Therefore, the child class plotter() must inherit the:

ax, and all its customized properties
fig

from the parent (myplot()) class. The following may change in the Child class:

  1. type of plot - scatter, line, etc.
  2. any properties of the type of plot - markersize, linestyle, etc.

but the matplotlib figure and axis objects (fig,ax) will always be required before plotting an input set of numbers.

Question:

Is this a correct usage of Python inheritance or is the child class superfluous in this case? If so, was there a place where the reasoning for a subclass was not consistent with object oriented programming (I'm having difficulty convincing myself of this)?

edesz
  • 11,756
  • 22
  • 75
  • 123
  • 1
    Why not just create a function that creates the figure and a second function which draws on it? In general you should prefer composition to inheritance. Inheritance should imply an "is a" relationship. Is "plotter" a "myplot"? – Zac Crites Jul 22 '16 at 20:17
  • No. it is not. Yeah, that pretty much answers my question. – edesz Jul 22 '16 at 23:07
  • Thanks for the reply. You basically just explained right here. I didn't understand this concept. – edesz Jul 22 '16 at 23:08
  • Also you should be inheriting your classes from `object` if they don't have a specific base type. In Python 3 you can omit the parentheses after the name of the class entirely if you like. – Zac Crites Jul 25 '16 at 15:21
  • Thanks. What do you mean by `object`? Do you have an example? – edesz Jul 25 '16 at 15:52
  • 1
    You should use `class myplot(object)`. Not subclassing from `object` will declare an [old-style class](http://stackoverflow.com/a/1203997/478079), which is something you do not want. Old-style classes were removed in Python 3, but some people still inherit from `object`, so it is optional. – Zac Crites Jul 25 '16 at 16:05
  • @WR: You should accept Zac's answer instead. Although your subclass does violate the Liskov Substitution Principle, it is due to semantics, not method signature. – Gerrat Jul 25 '16 at 17:07
  • @WR You might also want to give [PEP 8](https://www.python.org/dev/peps/pep-0008/) a read. For a quick taste, look at the code in my answer to see some of the code style fixes I made, particularly in your `if __name__ == '__main__':` section. – Zac Crites Jul 25 '16 at 17:14

2 Answers2

1

I think the main issue is that your subclass violates the Liskov Substitution Principle. Also see here for nice pictures of SOLID principles. You can't substitute an instance of myplot for plotter, since plotter's method signature is different. You should be able to substitute in a subclass instead of the base class. Because the signature is different, you cannot.

Gerrat
  • 28,863
  • 9
  • 73
  • 101
  • Yes, I get it now. I just missed it totally before. Thanks. – edesz Jul 22 '16 at 23:08
  • Child classes extending the functionality of a base class isn't a violation of LSP. – Zac Crites Jul 23 '16 at 15:05
  • @ZacCrites: You are correct. I have fixed the reasoning (His subclass still violates the LSP principle, but not for the reason I originally gave). – Gerrat Jul 25 '16 at 15:08
  • @Gerrat Is it? The constructors have different signatures, but there's nothing wrong with that. Do you mean that he's calling `mk_p` on an instance of `plotter` when he should really only be calling methods defined on the base class interface? – Zac Crites Jul 25 '16 at 15:18
  • @ZacCrites: The constructors do have different signatures, and there `is` something wrong with that. Because of that fact, you cannot use a `plotter` in place of a `myplot` (which is what LSP demands - see either article I linked, or any other resource). The extra `mk_p` method is ok - it wouldn't affect the substitution. – Gerrat Jul 25 '16 at 15:33
  • @Gerrat What's wrong with that? Once the object is created they can be used interchangably. If you had different `Logger` implementations, such as `FileLogger(string filepath)`, `DatabaseLogger(string usename, password, database_name, hostname)`, `ConsoleLogger()`, `WebServiceLogger(string username, password)`, obviously these all don't have the same constructors. The point of LSP is that each of these implementations can be *used* in the same way after they are created. If they all implement the `Logger` interface then it doesn't matter how they are created. – Zac Crites Jul 25 '16 at 15:59
  • @ZacCrites: It appears the internet agrees with you. I do a lot more functional programming than OO-based, and it appears my instincts with this were wrong. I would just delete my answer at this point rather than changing it again to point to the semantics of subclasses, but I can't since it's currently the accepted answer. Feel free to provide your own answer to this. – Gerrat Jul 25 '16 at 16:22
  • Wow, great points all around. I don't want to make this a long discussion but, I had asked the OP for clarification as to whether `plotter` would be a child of `myplot` the same way that `Parrot` would be the child of `Pet` as shown [here](http://www.jesshamrick.com/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/). I thought I had it perfectly set up but couldn't quite justify it to myself, which is why I had asked the question. I can see both points of view presented here. Thanks for all this info. – edesz Jul 29 '16 at 20:06
1

You should prefer composition to inheritance wherever possible. I would make a function that creates the plot and another that draws on it.

def create_plot(dims, pad):
    fig = plt.figure(figsize=(8.5, 11))
    ax = fig.add_axes(dims)
    ax.set_axisbelow(True)
    ax.tick_params(axis='x', pad=pad, which='both')
    return fig

def draw_plot(fig, x, y, outfile):
    fig.axes[0].plot(x, y, linestyle='-')
    plt.savefig(outfile, facecolor=fig.get_facecolor(), dpi=300)
    plt.show()

if __name__ == '__main__':
    x = np.arange(0.0, 2.0, 0.01)
    y = np.sin(2 * np.pi * x)
    dims = [0.60, 0.2, 0.2, 0.25]
    padding = 5

    outfile = 'Outfile.png'
    fig = create_plot(dims, padding)
    draw_plot(fig, x, y, outfile)
Zac Crites
  • 822
  • 10
  • 14