Python object composition - accessing a method from the class that called it - python

You'll have to forgive me, I am trying to teach myself OO but I have come across this problem with composition and 'has-a' relationships.
class Main(object):
def A(self):
print 'Hello'
def B(self):
self.feature = DoSomething()
class DoSomething(object):
def ModifyMain(self):
#Not sure what goes here... something like
Main.A()
def run():
M = Main()
M.B()
A real world example of the above simplification is a PySide application where Main is a MainWindow, and DoSomething is a dynamically created widget that is placed somewhere in the window. I would like DoSomething to be able to modify the status bar of the mainwindow, which is essentially calling (in Main) self.statusbar().
If there is a shortcut in PySide to do this, Tops!! please let me know! However, I'm actually after the more general Pythonic way to do this.
I think I'm close ... I just can't make it work...

Why don't you use a signal and slot instead? That's a more Qt and OOP way of doing this.
In your dynamically created widget class:
self.modifyMain = QtCore.Signal(str)
In your main class:
#QtCore.Slot(str)
def changeStatusbar(self, newmessage):
statusBar().showMessage(newmessage)
in you main class after creating your widget:
doSomething.modifyMain.connect(self.changeStatusbar)
And in you widget class, where you want to change the statusbar of main, you say:
modifyMain.emit("Hello")
None of this is tested as I don't have a PySide installation handy.

There are two problems with your code:
At no time do you call ModifyMain; and
Main.A() will result in an error, because A is an instance method, but you are calling it on a class.
You want something like:
class Main(object):
def A(self):
print 'Hello'
def B(self):
self.feature = DoSomething() # self.feature is an instance of DoSomething
self.feature.ModifyMain(self) # pass self to a method
class DoSomething(object):
def ModifyMain(self, main): # note that self is *this* object; main is the object passed in, which was self in the caller
#Note case - main, not Main
main.A()
def run():
M = Main()
M.B()
if __name__=="__main__": # this will be true if this script is run from the shell OR pasted into the interpreter
run()
Your names all flout the usual python conventions found in PEP8, which is a pretty good guide to python style. I have left them as they were in your code, but don't copy the style in this example - follow PEP8.

Related

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

python overriding method without re-copying whole code

I have a class Father whom I call run method to do the work. I inherit this class with a Child class. This look like this:
class Father:
def __init__(self):
...
def run(self):
work1()
work2()
....
class Child(Father):
def run(self):
pass
Usually, I use pass, as most of the time, children do same things as father, just being called from distinct contexts. However, sometimes the behavior changes. But when it does, work1, work2, and so on from Father are still being executed. Only a last workn should be added.
How is it possible to override run method without having to copy the whole code from Father.run, and just adding a last work instruction? I have tried this, which is working:
class Father:
def run(self):
work1()
...
run_additionnal_stuf()
def run_additionnal_stuf(self):
pass
class Child(Father):
def run_additionnal_stuf(self):
work()
However, is there any solution more elegant?
First of all, if your Child class doesn't change a method you don't need to define it with pass. Simply do nothing and thanks to inheritance magic, the child instance will also have that method.
As to your actual question, if you only want to add functionality to a child instance, you can use super. Indeed, from the docs:
This is useful for accessing inherited methods that have been
overridden in a class.
So you could do:
class Child(Father):
def run(self):
super().run()
work_n()
A simple demonstration:
class Father:
def run(self):
print("In Father")
class Child(Father):
def run(self):
super().run()
print("In Child")
f = Father()
c = Child()
f.run()
c.run()
And this expectedly prints out:
In Father
In Father
In Child

How can I call a Subclass object from a Superclass and is there a better way to do this? (Python 3)

I am creating a game in python(3) and I have a main class with the game loop and a bunch of variables in it. I have a subclass of this to create "path" objects, and I need the variables from the main class. I also need to call the subclass from the main, superclass. Every time I call the subclass, it also calls the main classes init method to pass variables through. The problem is when this happens, it resets the values of all my variables in the main class.
class Main:
def __init__(self):
foo = 0
sub1 = Sub()
def foo_edit(self):
self.foo += 5
def main(self):
sub2 = Sub()
class Sub(Main):
def __init__(self):
super(Sub, self).__init__()
self.bar = 0
def foo_edit(self):
self.foo += 10
I've looked at many other similar questions, but none have given me the answer I need. I tried sub1 in my code(in the init function of main) and this creates a recursion loop error because the init functions call eachother forever. When I call it in the gameloop(or main in this example) it re initializes Main each time it is called, wiping the variables needed in Sub. Before I only had one instance of the "path" so I had no need of class, and had a function. Now that I need multiple "path" objects I am using a subclass of main to get the variables I need.
A solution to this problem that does or does not answer my question(calling a subclass from a superclass might be a bad idea) would be appreciated. Thanks in advance.
EDIT: I could pass the variables needed into the "Sub" class, however I need them to be updated so I would need some sort of update method to make sure the variables passed in are changed in Sub when they are in Main. This is easily doable, but I was wondering if there was easier/better way to do it.
You are misunderstanding the purpose of subclasses. Inheritance isn't for sharing data, it's for making special cases of a general type.
If Sub should be exactly like Main with a few specializations then you should use inheritance. When you say that "Sub" is a subclass of "Main", you are saying "Sub is a Main', not "Sub uses part of Main".
If Sub merely needs access to data in Main, use composition. If Sub needs data from Main, pass an instance of Main to Sub, and have Sub operate on the instance.
For example:
class Main():
def __init__(self):
self.foo = 42
self.sub1 = Sub(self)
self.sub1.foo_edit()
...
class Sub():
def __init__(self, main):
self.main = main
def foo_edit(self):
self.main.foo += 10

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

PyQt4.QtCore.pyqtSignal object has no attribute 'connect'

I'm having issues with a custom signal in a class I made.
Relevant code:
self.parse_triggered = QtCore.pyqtSignal()
def parseFile(self):
self.emit(self.parse_triggered)
Both of those belong to the class: RefreshWidget.
In its parent class I have:
self.refreshWidget.parse_triggered.connect(self.tabWidget.giveTabsData())
When I try to run the program, I get the error:
AttributeError: 'PyQt4.QtCore.pyqtSignal' object has no attribute 'connect'
Help?
Thanks in advance.
I had the same exact problem as you.
Try moving
self.parse_triggered = QtCore.pyqtSignal()
out of your constructor but inside your class declaration. So instead of it looking like this:
class Worker(QtCore.QThread):
def __init__(self, parent = None):
super(Worker, self).__init__(parent)
self.parse_triggered = QtCore.pyqtSignal()
It should look like this:
class Worker(QtCore.QThread):
parse_triggered = QtCore.pyqtSignal()
def __init__(self, parent = None):
super(Worker, self).__init__(parent)
This might not be at all what you are looking for, but it worked for me. I switched back to old-style signals anyways because I haven't found a way in new-style signals to have an undefined number or type of parameters.
You also get that error message if you fail to call super() or QObject.__init__() in your custom class.
A checklist for defining custom signals in a class in Qt in Python:
your class derives from QObject (directly or indirectly)
your class __init__ calls super() (or calls QObject.__init__() directly.)
your signal is defined as a class variable, not an instance variable
the signature (formal arguments) of your signal matches the signature of any slot that you will connect to the signal e.g. () or (int) or (str) or ((int,), (str,))
I have recently started working with PySide (Nokia's own version of PyQt), and saw the exact same behaviour (and solution) with custom new-style signals. My biggest concern with the solution was that using a class variable to hold the signal would mess things up when I have multiple instances of that class (QThreads in my case).
From what I could see, QtCore.QObject.__init__(self) finds the Signal variable in the class and creates a copy of that Signal for the instance. I have no idea what QObject.__init__() does, but the resulting Signal does proper connect(), disconnect() and emit() methods (and also a __getitem__() method), whereas the class Signal or standalone Signal variables created outside of a QObject-derived class do not have these methods and can't be used properly.
To use the signal/slot system you need to have a QObject inherited class.
Here is a simple example:
from PySide import QtCore
class LivingBeing(QtCore.QObject):
bornSignal = QtCore.Signal() # initialise our signal
def __init__(self,name):
QtCore.QObject.__init__(self) # initialisation required for object inheritance
self.bornSignal.connect(self.helloWorld) # connect the born signal to the helloworld function
self.name = name #
self.alive = False
def summonFromClay(self):
self.alive = True
self.bornSignal.emit() # emit the signal
def helloWorld(self):
print "Hello World !, my name is %s, this place is so great !" % self.name
# now try the little piece of code
if __name__ == '__main__':
firstHuman = LivingBeing('Adam')
firstHuman.summonFromClay()
I had the same problem.
I forgot that if a class uses Signals, then it must inherit from QObject. I was doing some re-factoring and did not pay attention to this.
Why do you connect directly to the signal, while you can do
self.connect(widget, SIGNAL('parse_triggered()'), listener.listening_method)?
where self is, for example, the form itself, and may be the same as listener

Categories