Rahul Dev Mishra Rahul Dev Mishra - 26 days ago 17
Python Question

Python: How to call a child class's overridden method

I have got few classes which extend each other. And then I have another class which calls an overridden method

_draw(self.turtle)
.

Class structure:

class Canvas:
def __init__(self,w,h):
self.width = w
self.height = h
self.visibleObjects = []
self.turtle = turtle.Turtle()
self.screen = turtle.Screen()
self.screen.setup(width=self.width,height=self.height)
self.turtle.hideturtle()

def draw(self,gObject):
gObject.setCanvas(self)
gObject.setVisible(True)
self.turtle.up()
self.screen.tracer(0)
gObject._draw(self.turtle)
self.screen.tracer(1)
self.addShape(gObject)

class GeometricObject:
def __init__(self):
self.lineColor = 'black'
self.lineWidth = 1
self.visible = False
self.myCanvas = None

// setters and getters

class Shape(GeometricObject):
def __init__(self, fillColor = None):
super().__init__()
self.fillColor = fillColor

def setFill(self, aturtle):
aturtle.begin_fill()
aturtle.down()
aturtle.color(self.fillColor)


class Polygon(Shape):
def __init__(self, cornerPoints, color, lineColor, lineWidth):
super().__init__(color)
self.cornerPoints = cornerPoints

def _draw(self, aturtle):
// Start Drawing

class Triangle(Polygon):
def __init__(self, threePoints, fillColor = None, lineColor = None, lineWidth = None):
super().__init__(threePoints, fillColor, lineColor, lineWidth)
if (lineColor is not None):
self.lineColor = lineColor

if(lineWidth is not None):
self.lineWidth = lineWidth

def _draw(self, aturtle):
if (self.fillColor is not None):
self.setFill(aturtle)
aturtle.up()
Polygon._draw(self, aturtle)
aturtle.end_fill()

myCanvas = Canvas(800,600)
triangle = Triangle([Point(-50, -10), Point(150,25), Point(50,50)], "red", "yellow")
myCanvas.draw(triangle)


When
myCanvas.draw(triangle)
is called, it executes the draw method in the Canvas class. On the 6th line of draw method, I am calling the actual class's _draw method
gObject._draw(self.turtle)
. When I debugged at this point, gObject was of type Triangle. So when this line of code is executed I expect the control to go to Triangle's _draw(). However the control moves to Polygon's _draw() and the control never comes to Triangle's _draw().

I do not understand why is it executing Polygon's _draw() ? Can somebody please help me out here ? Is there something missing in the code.

p.s: I have multiple geometric objects class like Rectangle, Square which extend Polygon etc.

Answer

As mentioned in the comments, your indentions are wrong. For instance:

class Shape(GeometricObject):
    def __init__(self, fillColor = None):
        super().__init__()
        self.fillColor = fillColor

        def setFill(self, aturtle):
            aturtle.begin_fill()
            aturtle.down()
            aturtle.color(self.fillColor)

does NOT define a Shape.setFill method, but a setFill function that exists only in the Shape.__init__ method. So, for the same reason, the following code does not override the _draw method:

class Triangle(Polygon):
    def __init__(self, threePoints, fillColor = None, lineColor = None, lineWidth = None):
        super().__init__(threePoints, fillColor, lineColor, lineWidth)
        if (lineColor is not None):
            self.lineColor = lineColor

        if(lineWidth is not None):
            self.lineWidth = lineWidth

        def _draw(self, aturtle):
            if (self.fillColor is not None):
                self.setFill(aturtle)
                aturtle.up()
                Polygon._draw(self, aturtle)
                aturtle.end_fill()

For the "trivia", note that Python typing does not work as Java's or other's. In particular, there is no cast, unless you explicitly define it. So if you define Mother and Daughter as follows:

class Mother:
    def __init__(self):
        pass
    def printClass(self):
        print(self.__class__.__name__)

class Daughter(Mother):
    def __init__(self):
        super().__init__()

there is no direct way to get "Mother" printed when calling printClass on a Daughter object.

In Java, something like:

(Mother)Daughter.printClass();

would print "Mother", but you cannot do such things in Python.

In other words, a variable can be reassigned an object with a different type, but there is no way to change the type of a variable without reassigning it.

By the way, a workaround would be defining a castToMother method inside of the Daughter class, or something like this.

The point of this explanation, is to have you sure that if you know that your triangle object is of type Triangle, there is no way that triangle._draw would call Polygon._draw.

Comments