Jordi Torrents
Jordi Torrents

Reputation: 46

How to modify the same method in a set of sibling classes?

I have two classes (Table and Button) inherited from the same class Widget. Both subclasses have their own keyEvent() methods and both call Widget.keyEvent() when necessary. I want to modify the keyEvent() behaviour for both classes in the same way (for example make A and D keys to trigger LEFT and RIGHT keys).

This code works exactly as I want

class KeyModifier:
    def keyEvent():
        # some lines of code
        super().keyEvent()

class MyTable(KeyModifier,Table):
    pass

class MyButton(KeyModifier,Button):
    pass

But Pylance is angry because KeyModifier.super() doesn't have any keyEvent() method (which is true).

Is there a way to do it better? Also, I would like Pylance to warn me when using the KeyModifier with something not inherited from Widget.

This example comes from a PyQT app, but the question is more general.

Edit: Making KeyModifier a subclasss of Widget makes KeyModifier.super().keyEvent() call Widget.keyEvent() and I want to call the child class method (Table.keyEvent() or Button.keyEvent())

Upvotes: 0

Views: 80

Answers (3)

Jordi Torrents
Jordi Torrents

Reputation: 46

I've found a workaround as I don't really have to manage any class methods but the event that is being handled.

def KeyModifier(event: Event) -> Event:
    # some lines of code and edit 'event' if necessary
    return event

class MyButton(Button):
    def keyEvent(self, event: Event):
        super().keyEvent(KeyModifier(event))

I think this is the simplest way to write it. Thank you all for your suggestions :)

Upvotes: 0

matszwecja
matszwecja

Reputation: 8076

If you make KeyModifier inherit from Widget, the warning will be gone because keyEvent will actually be defined for the object. If you also add super().keyEvent() calls to your modified classes, all the proper events will fire thanks to something called MRO - Method Resolution Order.

class Base:
    def event(self):
        print("Base")

class A(Base):
    def event(self):
        print("A")

class B(Base):
    def event(self):
        print("B")

class Modifier(Base):
    def event(self):
        print("Modified")
        super().event()

class ModifiedA(Modifier, A):
    def event(self):
        print("ModifiedA")
        super().event()

class ModifiedB(Modifier, B):
    def event(self):
        print("ModifiedB")
        super().event()

ModifiedA().event()

Output:

ModifiedA
Modified
A

It is important to note that if A and B do not call a super on their own (I'm fairly certain that PyQt widgets DO call their parent though), Modifier has to be the first class inherited, as it will cause it to be first in MRO and have a chance to call the other class method in turn.

Upvotes: 0

John
John

Reputation: 11

Does it help?

from abc import abstractmethod


class Table:
    pass


class Button:
    pass


class KeyModifier:
    @abstractmethod
    def custom_operation(self):
        pass

    def key_event(self, condition):
        if condition:
            self.custom_operation()


class MyTable(KeyModifier, Table):
    def __init__(self):
        super(MyTable, self).__init__()

    def custom_operation(self):
        pass


class MyButton(KeyModifier, Button):
    def custom_operation(self):
        pass

Upvotes: 1

Related Questions