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

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())

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

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.

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 :)

Related

Python - Child Class to call a function from another Child Class

I have a pretty big class that i want to break down in smaller classes that each handle a single part of the whole. So each child takes care of only one aspect of the whole.
Each of these child classes still need to communicate with one another.
For example Data Access creates a dictionary that Plotting Controller needs to have access to.
And then plotting Controller needs to update stuff on Main GUI Controller. But these children have various more inter-communication functions.
How do I achieve this?
I've read Metaclasses, Cooperative Multiple Inheritence and Wonders of Cooperative Multiple Inheritence, but i cannot figure out how to do this.
The closest I've come is the following code:
class A:
def __init__(self):
self.myself = 'ClassA'
def method_ONE_from_class_A(self, caller):
print(f"I am method ONE from {self.myself} called by {caller}")
self.method_ONE_from_class_B(self.myself)
def method_TWO_from_class_A(self, caller):
print(f"I am method TWO from {self.myself} called by {caller}")
self.method_TWO_from_class_B(self.myself)
class B:
def __init__(self):
self.me = 'ClassB'
def method_ONE_from_class_B(self, caller):
print(f"I am method ONE from {self.me} called by {caller}")
self.method_TWO_from_class_A(self.me)
def method_TWO_from_class_B(self, caller):
print(f"I am method TWO from {self.me} called by {caller}")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
def children_start_talking(self):
self.method_ONE_from_class_A('Big Poppa')
poppa = C()
poppa.children_start_talking()
which results correctly in:
I am method ONE from ClassA called by Big Poppa
I am method ONE from ClassB called by ClassA
I am method TWO from ClassA called by ClassB
I am method TWO from ClassB called by ClassA
But... even though Class B and Class A correctly call the other children's functions, they don't actually find their declaration. Nor do i "see" them when i'm typing the code, which is both frustrating and worrisome that i might be doing something wrong.
Is there a good way to achieve this? Or is it an actually bad idea?
EDIT: Python 3.7 if it makes any difference.
Inheritance
When breaking a class hierarchy like this, the individual "partial" classes, we call "mixins", will "see" only what is declared directly on them, and on their base-classes. In your example, when writing class A, it does not know anything about class B - you as the author, can know that methods from class B will be present, because methods from class A will only be called from class C, that inherits both.
Your programming tools, the IDE including, can't know that. (That you should know better than your programming aid, is a side track). It would work, if run, but this is a poor design.
If all methods are to be present directly on a single instance of your final class, all of them have to be "present" in a super-class for them all - you can even write independent subclasses in different files, and then a single subclass that will inherit all of them:
from abc import abstractmethod, ABC
class Base(ABC):
#abstractmethod
def method_A_1(self):
pass
#abstractmethod
def method_A_2(self):
pass
#abstractmethod
def method_B_1(self):
pass
class A(Base):
def __init__(self, *args, **kwargs):
# pop consumed named parameters from "kwargs"
...
super().__init__(*args, **kwargs)
# This call ensures all __init__ in bases are called
# because Python linearize the base classes on multiple inheritance
def method_A_1(self):
...
def method_A_2(self):
...
class B(Base):
def __init__(self, *args, **kwargs):
# pop consumed named parameters from "kwargs"
...
super().__init__(*args, **kwargs)
# This call ensures all __init__ in bases are called
# because Python linearize the base classes on multiple inheritance
def method_B_1(self):
...
...
class C(A, B):
pass
(The "ABC" and "abstractmethod" are a bit of sugar - they will work, but this design would work without any of that - thought their presence help whoever is looking at your code to figure out what is going on, and will raise an earlier runtime error if you per mistake create an instance of one of the incomplete base classes)
Composite
This works, but if your methods are actually for wildly different domains, instead
of multiple inheritance, you should try using the "composite design pattern".
No need for multiple inheritance if it does not arise naturally.
In this case, you instantiate objects of the classes that drive the different domains on the __init__ of the shell class, and pass its own instance to those child, which will keep a reference to it (in a self.parent attribute, for example). Chances are your IDE still won't know what you are talking about, but you will have a saner design.
class Parent:
def __init__(self):
self.a_domain = A(self)
self.b_domain = B(self)
class A:
def __init__(self, parent):
self.parent = parent
# no need to call any "super...init", this is called
# as part of the initialization of the parent class
def method_A_1(self):
...
def method_A_2(self):
...
class B:
def __init__(self, parent):
self.parent = parent
def method_B_1(self):
# need result from 'A' domain:
a_value = self.parent.a_domain.method_A_1()
...
This example uses the basic of the language features, but if you decide
to go for it in a complex application, you can sophisticate it - there are
interface patterns, that could allow you to swap the classes used
for different domains, in specialized subclasses, and so on. But typically
the pattern above is what you would need.

Different behavior in base class __init__ depending on derived class

I have an abstract class Base containing a member boolean do_thing which will either trigger a one-time action on start-up, or do nothing. This variable can be overridden by a derived class Derived, but doing a super().__init__() call at the beginning of the Derived's __init__ leads to the one-time action being always based on what do_thing is set to in Base.
I only see two options for getting around this, neither of which seem ideal to me:
Call super().__init__() at the end of every derived class's __init__ rather than the beginning, which means I can't rely on other default variables set in Base.
Explicitly call the one-time action at the end of every derived class's __init__, which means either duplicated code, or an extra function in Base that will only ever be called at startup.
Some example code
from abc import ABC
class Base(ABC):
def __init__(self):
self.do_thing = False
# Want to wait for child class init before running this
if self.do_thing:
configuration.set(do_thing_parameters)
class Derived(Base):
def __init__(self):
super().__init__()
# Should have configs properly set based on this being true
self.do_thing = True
class RegularDerived(Base):
def __init__(self):
super().__init__()
# Don't modify the config
Is there a better way of doing this that I'm missing?
From your description, it sounds like your do_thing functionality is to do with your classes, not your instances. If that's so, it doesn't seem right to have it as a parameter to __init__. You have other options, and I'd go with
A class attribute
class Base:
_do_thing = False
def __init__(self):
if self._do_thing:
configuration.set(do_thing_parameters)
class Derived(Base):
_do_thing = True
class RegularDerived(Base):
pass
Then you don't even need to define __init__ in the subclasses
Try setting the "do_thing" variable as a default parameter, as shown below...
from abc import ABC
class Base(ABC):
def __init__(self, do_thing=False):
if do_thing:
configuration.set(do_thing_parameters)
class Derived(Base):
def __init__(self):
super().__init__(True)

inheriting python method with a call to super inside the method

I am developing a system, which has a series of single multilevel inheritance hierarachy. one of the methods (applicable to all the classes) has to perform the same thing for most of the classes, which is to pass a list to its parent class.
I know that if one doesn't define a method in one of the inherited classes, its parents' methods are used. But when we use the super method, we need to mention the name of the class being called.
One method I know to achieve this is to redefine the method at every class with class name as argument. Is there any elegant method where I can define it once at the topmost parent, and then override it only when necessary?
The implementation right now looks like this
class a(object):
def __init__(self):
self.myL = list()
print 'hello'
class b(a):
def __init__(self):
super(b,self).__init__()
def resolve(self, passVal):
print passVal
self.myL.append(passVal)
super(b,self).resolve(passVal+1)
class c(b):
def __init__(self):
super(c,self).__init__()
def resolve(self, passVal):
print passVal
self.myL.append(passVal)
super(c,self).resolve(passVal+1)
Instead if I can define resolve in class a, and then all other classes inherit the method from it. I understand a will never be able to use it. but redefining the method seems a lot unnecessary extra work.

Adding a method to a class within a class

Complete brain fart here and not even sure I am asking the right question. How do I add/change a method of a class that exists within a class?
I am building a QT GUI designed in QtDesigner. My Python program imports and makes a new class subclassed to the GUI file class. I want to change a method to a button within that class.
So basically I have the below, and I want to add a method to 'aButton'.
qtDesignerFile.py
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.aButton = QtGui.QPushButton()
myPythonFile.py
import qtDesignerFile
class slidingAppView(QMainWindow,slidingGuiUi.Ui_MainWindow):
def __init__(self,parent=None):
super(slidingAppView,self).__init__(parent)
To add to Joran's answer, methods added like this:
def foo():
pass
instance.foo = foo
will act like static methods (they won't have the instance passed as first argument). If you want to add a bound method, you can do the following:
from types import MethodType
def foo(instance):
# this function will receive the instance as first argument
# similar to a bound method
pass
instance.foo = MethodType(foo, instance, instance.__class__)
self.aButton.PrintHello = lambda : print "hello!"
or
def aMethod():
do_something()
self.aButton.DoSomething = aMethod
either should work... probably more ways also ... this assumes aButton is a python class that inherits from Object

Init child with Parent instance

I have a function which return instances of the class Parent:
def generateParent():
do_stuff
return Parent(some_parameters)
Now I want to init a subclass of Parent with the results of a call to generateParent():
class Child(Parent):
def __new__():
return generateParent(some_other_parameters)
The problem is, when I override some methods from Parent in Child and then call them in instances of Child in my program, the original Parent method gets called instead of the new one from Child. Am I doing something wrong here? Am I using the correct design here for my task?
EDIT: I don't have access neither to Parent nor generateParent()
Solution(thanks to #Paul McGuire's answer):
class Child(object):
def __init__(self):
self.obj = generateParent()
def __getattr__(self, attr):
return getattr(self.obj, attr)
Since generateParent is not your code, then instead of inheritance, you might want to use containment and delegation. That is, instead of defining a subclass, define a wrapper class that contains the generated object, forwards method calls to it when needed, but can add new behavior or modified behavior in the wrapper.
In this question, the OP had a similar situation, having a class generated in a libary, but wanting to extend the class and/or modify some behavior of the class. Look at how I added a wrapper class in that question, and you might consider doing something similar here.
Here's one way to do it:
def generateChild(params):
p = generateParent(params)
p.__class__ = Child
return p
class Child(Parent):
# put method overrides etc here
childinstance = generateChild(some_params)
Perhaps you want generateParent to be able to make instances of other classes:
def generateParent(cls=Parent):
do_stuff
return cls(some_parameters)
Now this will make a Child object:
child = generateParent(Child)
Or perhaps you want Parent and all of its derived classes to use common initialization code?
class Parent(object):
def __init__(self):
do_stuff
# init from some_parameters
class Child(Parent):
# blah..
Make your Child object able to copy information from a created Parent object:
class Child(Parent):
def __init__(self):
model_parent = generateParent()
self.a = model_parent.a
self.b = model_parent.b
# etc.

Categories