I'm creating a GUI for a project of mine using Python. Even though this is a private project, I would like to use good coding practices. First, let me introduce a simplified version of my GUI module:
# Just a box, can have borders or it can be filled
class Box(object):
def __init__(self):
# Set initial state
def update(self, xy, press):
# I'm just a dummy box, I don't care about xy or press
pass
def draw(self):
# Draw
# Like a box but with special functionality
class Button(Box):
def __init__(self):
super(Button, self).__init__()
# Set initial state
def update(self, xy, press):
# Do something with xy and press
# Like a box but with special functionality
class Status(Box):
def __init__(self):
super(Status, self).__init__()
# Set initial state
def update(self, xy, press):
# Do something with xy, ignore press
# A box which can hold boxes inside it to group them
class Container(Box):
def __init__(self):
super(Container, self).__init__()
self.childs = deque()
def update(self, xy, press):
for c in self.childs:
c.update(xy, press)
# Container draws itself like a Box but also draws boxes inside it
def draw(self):
super(Container, self).draw()
for c in self.childs:
c.draw()
Every GUI component is in a container. Container's update() gets called every cycle to update the state of those components with the latest input information.
I like this solution since it allows me to use an interface to update the whole GUI in one loop and it saves a bit of code. My problem is that some of these childs need more information than others to update their state which leads into unused parameters with the use of the interface.
So, is the use of unused parameters considered a bad practice in this case and should I just give up using the interface?
The usual way to do this is called cooperative inheritance, which is essentially just a buzzword to say that both the super- and the sub- class expect each other to be around and be passed information it may not need. Methods of this type tend to look like:
def foo(self, specific, arguments, *args, **kwargs):
do_something_with(specific, arguments)
super(MyClass, self).foo(*args, **kwargs)
in other words, each more-specific Container handles what's special about it, but if there's default functionality that's common to all Containers (and if there isn't -- why are we using inheritance?!) then you define that only in the superclass and use super to defer to it in the subclass.
Related
I want to define a subclass of a class, and I need it to be upgraded to in one of the class' methods, rather than initialized from the very beginning.
The example is a hobby project with Grids and the distances in them.
So I start a Grid class like this:
class Grid:
def __init__(self):
# A grid is a list of lists of cells.
self.grid = self.prepare_grid()
self.configure_cells()
self.maze = None
def __getitem__(self, a_tuple):
x, y = a_tuple
return self.grid[x][y]
def prepare_grid(self):
...
def configure_cells(self):
...
Then I want to add Distance functionality. I was thinking like in a game where you've improved to became a Distance Grid, with methods like:
class DistanceGrid(Grid):
def set_distances(self, root_cell):
...
def path_to(self, goal):
...
Here comes the tricky part. Since I don't want it to be initialized, but rather improved to, I need a method on the parent class to make it a subclass (looks almost recursive, but hopefully it isn't).
from distances import DistanceGrid
class Grid:
...
def upgrade_distance(self, root):
self = type(self, DistanceGrid)
self.set_distances(root)
Can this be done? I'll keep trying.
Thanks
You say, "Since I don't want it to be initialized, but rather improved to, I need a method on the parent class to make it a subclass". Have you ever heard of composition as an alternative to inheritance? It would make it so that you have a method on the parent class to give it a DistanceGrid instance, as one of the class attributes. Something like this:
from distances import DistanceGrid
class Grid:
...
def upgrade_distance(self, root):
self.distance_grid = DistanceGrid()
self.distance_grid.set_distances(root)
The rest of the methods in the Grid class could either act on self.distance_grid if it is a method to be run after the upgrade, or ignore it if it's not needed. Maybe that would work for you?
Edit: You could also make 3 classes; 1-DistanceGrid, 2-Grid, 3-Choice, where the Choice class is the only one you create, it inherits from both of the other classes, and you decide which methods you want to call as you go.
Is it possible to modify/extend an inherited method from the middle. I realize I can call super and get the original method, then either put code before or after that call which will extend the original. Is there a technique of doing something similar but from the middle of a method?
class Base():
def __init__(self):
self.size = 4
def get_data(self):
data = []
for num in range(self.size):
data.append("doing stuff")
data.append("doing stuff")
### add here from child##
data.append("doing stuff")
data.append("doing stuff")
return data
class MyClass(Base):
def __init__(self):
super().__init__()
def get_data(self):
# inherited parent code
# Do something else here
# inherited parent code
Despite Python's powerful introspection and code-modifying capabilities, there is no "clean" way of doing this. It could be done only by directly modifying the bytecode in the original function and shoehorsing a new method call in there - which would also implying in creating a new code and function objects - definitely not something to do in production code - even because bytecode is not guaranteed to be unchanged across Python versions or Python implementations.
Refactoring the original method:
But it can be done if the original method is coded in a way it is "aware" of points were subclasses might want to run additional code (maybe even being split up in several methods):
For your example, you'd have something like:
class Base():
def __init__(self):
self.size = 4
def get_data(self):
self.data = data = []
for num in range(self.size):
data.append("doing stuff")
data.append("doing stuff")
self.do_extra_things_with_data()
data.append("doing stuff")
data.append("doing stuff")
return data
def do_extra_things_with_data():
"""Override this on subclasses"""
class MyClass(Base):
def __init__(self):
super().__init__()
def do_extra_things_with_data():
print(len(self.data), "objects defined so far")
One technical name for this is "slot". (It is used for templating in certain web frameworks - the derived page uses the parent template for columns and general layout, and defines "slots" for the content areas)
One other thing to watch are descriptors such as "properties": you can't change the superclass'method code - but if the code retrieves instance attributes for its computations, you can define these attributes as properties on the subclasses to run custom code.
Using descriptors:
One other way of doing that is to use descriptors such as "properties": you can't change the superclass'method code - but if the code retrieves instance attributes for its computations, you can define these attributes as properties on the subclasses to run custom code.
Let's suppose your method makes use of the self.size attribute, but it is exactly for calculating it that you might want to run more code - keeping exactly the same Base class you can do:
class MyClass(Base):
#property
def size(self):
# put extr calculation to retrieve the dynamc value
of self.size here
return value
Is there a technique of doing something similar but from the middle of
a method?
Not really. The def compiles into a function object that has a self-contained code object that is usually treated as being opaque.
When a need like this arises, it is usually an indication that the parent method needs to be split into reusable components that can be called separately.
If you can't refactor the parent method, then the unfortunate alternative is that the subclass will have to override the method and duplicate some of the code from the parent.
In short, Pythonic object oriented design is treats methods and attributes as the atomic units of composability.
I am working with the Python canmatrix library (well, presently my Python3 fork) which provides a set of classes for an in-memory description of CAN network messages as well as scripts for importing and exporting to and from on-disk representations (various standard CAN description file formats).
I am writing a PyQt application using the canmatrix library and would like to add some minor additional functionality to the bottom level Signal class. Note that a CanMatrix organizes it's member Frames which in turn organize it's member Signals. The whole structure is created by an import script which reads a file. I would like to retain the import script and sub-member finder functions of each layer but add an extra 'value' member to the Signal class as well as getters/setters that can trigger Qt signals (not related to the canmatrix Signal objects).
It seems that standard inheritance approaches would require me to subclass every class in the library and override every function which creates the library Signal to use mine instead. Ditto for the import functions. This just seems horribly excessive to add non-intrusive functionality to a library.
I have tried inheriting and replacing the library class with my inherited one (with and without the pass-through constructor) but the import still creates library classes, not mine. I forget if I copied this from this other answer or not, but it's the same structure as referenced there.
class Signal(QObject, canmatrix.Signal):
_my_signal = pyqtSignal(int)
def __init__(self, *args, **kwargs):
canmatrix.Signal.__init__(self, *args, **kwargs)
# TODO: what about QObject
print('boo')
def connect(self, target):
self._my_signal.connect(target)
def set_value(self, value):
self._my_value = value
self._my_signal.emit(value)
canmatrix.Signal = Signal
print('overwritten')
Is there a direct error in my attempt here?
Am I doing this all wrong and need to go find some (other) design pattern?
My next attempt involved shadowing each instance of the library class. For any instance of the library class that I want to add the functionality to I must construct one of my objects which will associate itself with the library-class object. Then, with an extra layer, I can get from either object to the other.
class Signal(QObject):
_my_signal = pyqtSignal(int)
def __init__(self, signal):
signal.signal = self
self.signal = signal
# TODO: what about QObject parameters
QObject.__init__(self)
self.value = None
def connect(self, target):
self._my_signal.connect(target)
def set_value(self, value):
self.value = value
self._my_signal.emit(value)
The extra layer is annoying (library_signal.signal.set_value() rather than library_signal.set_value()) and the mutual references seem like they may keep both objects from ever getting cleaned up.
This does run and function, but I suspect there's still a better way.
I am using a python package (simpy), which provides several classes that I need. One of the classes is called Event, with the following constructor:
def __init__(self, env):
self.env = env
"""The :class:`~simpy.core.Environment` the event lives in."""
self.callbacks = []
"""List of functions that are called when the event is processed."""
self._value = PENDING
At many different places in the code, objects are added to the callbacks of an event, using the Event.callbacks.append method.
What I need is a new class (which i call Zombie), which is actually an Event class, except for three modifications. Firstly, it should contain an additional attribute Zombie.reset_callbacks and a method Zombie.reset() to reset Zombie.callbacks to a previous state (this is why I need the Zombie.reset_callbacks attribute. All of this, I can do by subclassing Event.
However, for this to work, I would need that everytime Zombie.callbacks.append(x) is called, xis not only appended to Zombie.callbacks, but also Zombie.reset_callbacks. I have been looking into decorators to see if I could do this, but I do not see the light at the end of the tunnel. I currently feel this is not possible, or I might be looking in wrong directions.
Is such thing possible (changing the append behavior for a class attribute) in Python? And if so, how?
Thanx for your effort in advance!
B.
Whoops. Misread this. If you're really dedicated to maintaining this interface, you can define a helper class.
class SplitLists(object):
def __init__(*append_methods):
self._append_methods = append_methods
def append(self, value):
for method in self._append_methods:
method(value)
a = []
b = []
split_list = SplitLists(a.append, b.append)
split_list.append(1)
a # [1]
b # [1]
class Zombie(Event):
def __init__(self, *args, **kwargs):
super(Zombie, self).__init__(*args, **kwargs)
self._real_callbacks = []
self._reset_callbacks = []
self.callbacks = SplitLists(self._real_callbacks.append,
self._reset_callbacks.append)
I'm making a GUI with pyqt4 and python. Right now I have a QLineEdit and QComboBox, where the QLineEdit displays the values and the QComboBox can be used to change units. I'm using signals and slots to handle real time unit/value feedback for the user but I'm having problems understanding how to programmatically work with the values as I need them all to be in standard units. Here's what I've got so far, the combo_box_line_edit_list is a list of list where I wrap the combo box and line list together
class UnitConverterSignaler(QtCore.QObject):
def __init__(self, combo_box_line_edit_list):
super(QtCore.QObject, self).__init__()
self.combo_box_line_edit_list = combo_box_line_edit_list
self.combo_box_list = [line_edit_combo_box[0] for line_edit_combo_box in combo_box_line_edit_list]
for combo_box, line_edit in self.combo_box_line_edit_list:
combo_box.currentIndexChanged['QString'].connect(line_edit.convert_units)
line_edit.store_unit_state(combo_box.currentText())
line_edit.standard_unit = combo_box.itemText(1)
def convert_to_standard(self):
for combo_box in self.combo_box_list:
combo_box.setCurrentIndex(0)
def convert_to_international(self):
for combo_box in self.combo_box_list:
combo_box.setCurrentIndex(1)
def toggle_unit_conversion(self, hold_line_values_steady):
for combo_box in self.combo_box_list:
if hold_line_values_steady:
combo_box.do_not_convert_units_on_change()
else:
combo_box.convert_units_on_change()
def convert_units_on_change(self):
"""
Changes the value of the line edit each time the combo box is changed
"""
for combo_box, line_edit in self.combo_box_line_edit_list:
combo_box.currentIndexChanged['QString'].connect(line_edit.convert_units)
combo_box.currentIndexChanged['QString'].disconnect(line_edit.store_unit_state)
def do_not_convert_units_on_change(self):
"""
Holds the line edit value constant in spite of combo box changes
"""
for combo_box, line_edit in self.combo_box_line_edit_list:
combo_box.currentIndexChanged['QString'].disconnect(line_edit.convert_units)
combo_box.currentIndexChanged['QString'].connect(line_edit.store_unit_state)
Instantiated & used in another class
self.lockCellCheckBox.toggled.connect(self.unit_converter_signaler.toggle_unit_conversion)
self.internationalPushButton.clicked.connect(self.unit_converter_signaler.convert_to_international)
self.standardPushButton.clicked.connect(self.unit_converter_signaler.convert_to_standard)
I've also monkey patched the QLineEdit instead of subclassing so I can make quick changes with QtDesigner.
# monkey patch slot onto line_edit
def convert_units(line_edit, end_unit):
converted_unit_value = line_edit.unit_registry.convert(float(line_edit.text()), line_edit.stored_unit_state, str(end_unit))
line_edit.setText(str(converted_unit_value))
line_edit.stored_unit_state = str(end_unit)
# monkey patch slot onto line_edit
def store_unit_state(line_edit, unit):
line_edit.stored_unit_state = str(unit)
Would the most generalized way to get the standard units out in my main program be the creation of a signal for each combo box/line edit in the UnitConverter?
From what I understood so far: you have many combo-box/line-edit pairs and the entered values should always be converted to standard units (e.g. displayed on a third QLabel or whatever).
Would the most generalized way to get the standard units out in my main program be the creation of a signal for each combo box/line edit in the UnitConverter?
No, you don't have to. A slot in python (or especially in pyqt) can be any callable object. A callable object is an object with method __call__(self) implemented.
Therefore I would suggest you to create a class which takes the related object(s) as parameter(s) in the contructor and changes them in __call__(self). Something like this:
class ConverterSignal:
def __init__(whatever_you_want_to_refer_to):
self.whatever_you_want_to_refer_to = whatever_you_want_to_refer_to
def __call(self)__:
""" Here you can refer to whatever_you_want_to_refer_to and do whatever you want with it """
The connection is done as following (for the combo box as an example):
self.connect(combo_box, QtCore.SIGNAL('activated(int)'), ConverterSignal(whatever_you_want_to_refer_to))
Here an instance of the class ConverterSignal is created and will be called if the corresponding signal is emitted.