[Python] Override method in child class using less arguments than base class

Discussão em 'Python' iniciado por Stack, Agosto 25, 2025.

  1. Stack

    Stack Membro Participativo

    I am building an app (Weather report) using tkinter and Python 3.6.

    I have built a base class for objects to be placed on tkinter canvas in relation to other objects already existing on the canvas. The idea is to be able to easily stick them next to something else already on the canvas without absolute coordinates.

    I have a method move_rel_to_obj_y which purpose is to centre y coordinate of an instance of the child class on centre of y coordinate of a relative object already present on the canvas.

    The base method is supposed to be used only by the child class. There are already 2 child classes and I want to avoid copy-pasting this method into them.

    Now in the base class the method takes (self, obj, rel_obj) as arguments. But the self in the base class is not the instance of the child class on which I want to operate (move the image on canvas) so my overridden method takes (self, rel_obj) as we assume now that obj becomes self (instance of the child class) which I want to move.

    Therefore I came up with the solution that is below.

    My question is:


    1. Is there any other way to pass child instance to the base method without doing what I did and that way is more elegant?


    2. In the override of the base method I use less arguments than in the base class and the signature of the method changes (as Pycharm warns :)). I added **kwargs to keep the amount of arguments same but it would work without doing that (I tested and it is not needed). What is the appropriate way of actually doing that kind of inheritance when amount of arguments changes? Should I avoid it at all costs? If we should avoid it then how to solve my problem of needing to pass child instance to the base method?

    Base class:

    class CanvasObject(object):
    """Base class to create objects on canvas.

    Allows easier placement of objects on canvas in relation to other objects.

    Args:
    object (object): Base Python object we inherit from.

    """

    def __init__(self, canvas, coordinates=None, rel_obj=None,
    rel_pos=None, offset=None):
    """Initialise class - calculate x-y coordinates for our object.

    Allows positioning in relation to the rel_obj (CanvasText or CanvasImg object).
    We can give absolute position for the object or a relative one.
    In case of absolute position given we will ignore the relative parameter.
    The offset allows us to move the text away from the border of the relative object.

    Args:
    canvas (tk.Canvas): Canvas object to which the text will be attached to.
    image (str): String with a path to the image.
    coordinates (tuple): Absolute x, y coordinates where to place text in canvas. Overrides any parameters
    given in relative parameters section.
    rel_obj (CanvasText / CanvasImg): CanvasText / CanvasImg object which will be used
    as a relative one next to which text is meant to be written.
    rel_pos (str): String determining position of newly created text in relation to the relative object.
    Similar concept to anchor.
    TL - top-left, TM - top-middle, TR - top-right, CL - center-left, CC - center-center,
    CR - center-right, BL - bottom-left, BC - bottom-center, BR - bottom-right
    offset (tuple): Offset given as a pair of values to move the newly created object
    away from the relative object.

    :Attributes:
    :canvas (tk.Canvas): tkinter Canvas object.
    :pos_x (int): X coordinate for our object.
    :pos_y (int): Y coordinate for our object.

    """
    self.canvas = canvas
    pos_x = 0
    pos_y = 0

    if offset:
    offset_x = offset[0]
    offset_y = offset[1]
    else:
    offset_x = 0
    offset_y = 0
    if coordinates:
    pos_x = coordinates[0]
    pos_y = coordinates[1]
    elif rel_obj is not None and rel_pos is not None:
    # Get Top-Left and Bottom-Right bounding points of the relative object.
    r_x1, r_y1, r_x2, r_y2 = canvas.bbox(rel_obj.id_num)
    # TL - top - left, TM - top - middle, TR - top - right, CL - center - left, CC - center - center,
    # CR - center - right, BL - bottom - left, BC - bottom - center, BR - bottom - right

    # Determine position of CanvasObject on canvas in relation to the rel_obj.
    if rel_pos == "TL":
    pos_x = r_x1
    pos_y = r_y1
    elif rel_pos == "TM":
    pos_x = r_x2 - (r_x2 - r_x1) / 2
    pos_y = r_y1
    elif rel_pos == "TR":
    pos_x = r_x2
    pos_y = r_y1
    elif rel_pos == "CL":
    pos_x = r_x1
    pos_y = r_y2 - (r_y2 - r_y1) / 2
    elif rel_pos == "CC":
    pos_x = r_x2 - (r_x2 - r_x1) / 2
    pos_y = r_y2 - (r_y2 - r_y1) / 2
    elif rel_pos == "CR":
    pos_x = r_x2
    pos_y = r_y2 - (r_y2 - r_y1) / 2
    elif rel_pos == "BL":
    pos_x = r_x1
    pos_y = r_y2
    elif rel_pos == "BC":
    pos_x = r_x2 - (r_x2 - r_x1) / 2
    pos_y = r_y2
    elif rel_pos == "BR":
    pos_x = r_x2
    pos_y = r_y2
    else:
    raise ValueError("Please use the following strings for rel_pos: TL - top - left, "
    "TM - top - middle, TR - top - right, CL - center - left,"
    " CC - center - center, CR - center - right, BL - bottom - left, "
    "BC - bottom - center, BR - bottom - right")
    self.pos_x = int(pos_x + offset_x)
    self.pos_y = int(pos_y + offset_y)

    def move_rel_to_obj_y(self, obj, rel_obj):
    """Move obj relative to rel_obj in y direction.
    Initially aligning centers of the vertical side of objects is supported.

    Args:
    obj (CanvasText | CanvasImg): Object which we want to move.
    rel_obj (CanvasText | CanvasImg): Object in relation to which we want to move obj.

    Returns:
    None

    """
    # Find y coordinate of the center of rel_obj.
    r_x1, r_y1, r_x2, r_y2 = self.canvas.bbox(rel_obj.id_num)
    r_center_y = r_y2 - (r_y2 - r_y1) / 2

    # Find y coordinate of the center of our object.
    x1, y1, x2, y2 = self.canvas.bbox(obj.id_num)
    center_y = y2 - (y2 - y1) / 2

    # Find the delta.
    dy = int(r_center_y - center_y)

    # Move obj.
    self.canvas.move(obj.id_num, 0, dy)
    # Update obj pos_y attribute.
    obj.pos_y += dy


    Child class:

    class CanvasImg(CanvasObject):
    """Creates image object on canvas.

    Allows easier placement of image objects on canvas in relation to other objects.

    Args:
    CanvasObject (object): Base class we inherit from.

    """

    def __init__(self, canvas, image, coordinates=None, rel_obj=None,
    rel_pos=None, offset=None, **args):
    """Initialise class.

    Allows positioning in relation to the rel_obj (CanvasText or CanvasImg object).
    We can give absolute position for the image or a relative one.
    In case of absolute position given we will ignore the relative parameter.
    The offset allows us to move the image away from the border of the relative object.
    In **args we place all the normal canvas.create_image method parameters.

    Args:
    canvas (tk.Canvas): Canvas object to which the text will be attached to.
    image (str): String with a path to the image.
    coordinates (tuple): Absolute x, y coordinates where to place text in canvas. Overrides any parameters
    given in relative parameters section.
    rel_obj (CanvasText / CanvasImg): CanvasText / CanvasImg object which will be used
    as a relative one next to which text is meant to be written.
    rel_pos (str): String determining position of newly created text in relation to the relative object.
    Similar concept to anchor.
    TL - top-left, TM - top-middle, TR - top-right, CL - center-left, CC - center-center,
    CR - center-right, BL - bottom-left, BC - bottom-center, BR - bottom-right
    offset (tuple): Offset given as a pair of values to move the newly created text
    away from the relative object.
    **args: All the other arguments we need to pass to create_text method.

    :Attributes:
    :id_num (int): Unique Id number returned by create_image method which will help us identify objects
    and obtain their bounding boxes.

    """
    # Initialise base class. Get x-y coordinates for CanvasImg object.
    super().__init__(canvas, coordinates, rel_obj, rel_pos, offset)

    # Prepare image for insertion. Should work with most image file formats.
    img = Image.open(image)
    self.img = ImageTk.PhotoImage(img)
    id_num = canvas.create_image(self.pos_x, self.pos_y, image=self.img, **args)
    # Store unique Id number returned from using canvas.create_image method as an instance attribute.
    self.id_num = id_num

    def move_rel_to_obj_y(self, rel_obj, **kwargs):
    """Move instance in relation to rel_obj. Align their y coordinate centers.
    Override base class method to pass child instance as obj argument automatically.


    Args:
    rel_obj (CanvasText | CanvasImg): Object in relation to which we want to move obj.

    **kwargs (): Not used

    Returns:
    None
    """
    super().move_rel_to_obj_y(self, rel_obj)

    Continue reading...

Compartilhe esta Página