Python - Propagating added base class functionalty to subclasses - python

Say I am using a shape-drawing framework with the following classes:
class Shape(ABC):
movable: False
#abstractmethod
def movePoint(pointId, newX, newY): pass
class Rect(Shape):
aspectLocked: False
# Implement abstract function
def movePoint(pointId, newX, newY): pass
class Hexagon(Shape): pass
# ...
I want to use Rect, Hexagon, etc. in my code, but I want each to have a fill() method. However, fill() can work at the Shape level, so I end up doing this:
def _shapeFill(self):
pass
class MyRect(Rect):
def fill(self):
return _shapeFill(self)
# Same procedure for Hexagon, etc.
I can also do
for shape in [Rect, Hexagon]:
shape.fill = _shapeFill
But editors like PyCharm don't recognize fill() as a class method and I don't know if this solution is robust.
Basically, I want to extend Shape AND have that extended method available in the Rect class without having to redefine Rect. Is there a pythonic solution?
Bonus points if someone can tell me the technical name for this, since I didn't know what term to Google. Thanks!

You can just attach it to the ABC:
Shape.fill = _shapefill
If you want to play more nicely with IDEs and code analysis, you could define your extra methods in a mixin class which you inherit from in custom shape classes:
class ShapeMixin:
def fill(self):
pass
class MyRect(Rect, ShapeMixin):
pass
This is basically your first solution, but expressed a little more cleanly.

Related

Multiple function forms in template pattern

I wanted to ask what is the best way to implement template pattern in which template method can have multiple forms (I guess it wouldn't be template pattern then).
Let's say I have an abstract class with one abstract method and few concrete methods:
from abc import ABC, abstractmethod
from typing import Any
class TemplateClass(ABC):
def __init__(self, my_client):
self.client = my_client
def run(self) -> Any:
self._step1()
self._step2()
self._step3()
self.execute_specific_logic()
#abstractmethod
def _execute_specific_logic(self) -> Any:
raise NotImplementedError
def _step1(self):
pass
def _step2(self):
pass
def _step3(self):
pass
And I want to create about 10 classes that will inherit TemplateClass, but:
7 of them should have run method with all steps
2 of them should have run method only with _step2 and _step3
1 of them should have run method only with _step1
I was wondering about different ways to implement such logic:
implementing different run methods for every case - run, run_without_step1, run_without_step2_and_step3
adding flag argument to run method such as is_step_x_required with True as default and passing Flase in subclass method when needed
overwriting in run method in subclasses when needed
using some kind of mixin class?
I would really appreciate any advice on this issue.
All the techniques you list are reasonable. My first inclination was towards the "least" amount of boilerplate:
class Stepper(ABC):
def __init__(self, run_steps):
self._run_steps = run_steps
self._steps = [self._step1, self._step2, self._step3]
def run(self):
for step in self._run_steps:
self._steps[i - 1]()
class Only23Stepper(Stepper):
def __init__(self):
super().__init__(run_steps=[2, 3])
For more flexibility, but more boilerplate, one may override run() to explicitly specify the steps.
However, we may want to express this in a different way than with classes. It is well known that inheritance is evil. It is possible that the reason we're running into design issues and potential inflexibility is that this could be expressed in a simpler way using plain old functions.

How can I add class inheritance in a method outside initialization?

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.

Factory calling alternate constructor (classmethod)

I am struggling finding a way to have a class Factory (I use factory_boy version 2.11.1 with Python 3) using an alternate constructor defined as a #classmethod.
So let's say we have a class for building a 2D-point object with a default constructor and 2 additional ones:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
#classmethod
def fromlist(cls, coords): # alternate constructor from list
return cls(coords[0], coords[1])
#classmethod
def duplicate(cls, obj): # alternate constructor from another Point
return cls(obj.x, obj.y)
I create a basic Point factory:
import factory
class PointFactory(factory.Factory):
class Meta:
model = Point
inline_args = ('x', 'y')
x = 1.
y = 2.
By default, it seems to call the constructor __init__ of the class which seems very logical. I could not find a way to pass inline_args as being coords for using the alternate constructor fromlist. Is there a way to do so?
This is my first experience working and building factories in general so I may also be looking up at the wrong keywords on the web...
The point of factory_boy is to make it easy to produce test instances. You'd just call PointFactory() and you are done, you have test instances for the rest of your code. This usecase doesn't need to use any of the alternative constructors, ever. The factory would just use the main constructor.
If you are thinking that factory_boy factories must be defined to test your extra constructors, then you have misunderstood their use. Use factory_boy factories to create test data for other code to be tested. You'd not use them to test the Point class (other than to generate test data to pass to one of your constructors).
Note that inline_args is only needed if your constructor doesn't accept keyword arguments at all. Your Point() class has no such restriction; x and y can be used both as positional and as keyword arguments. You can safely drop inline_args from your definition, the factory will work regardless.
If you must use one of the other constructors (because you can't create test data with the main constructor), just pass the specific constructor method in as the model:
class PointListFactory(factory.Factory):
class Meta:
model = Point.fromlist
coords = (1., 2.)

Python super() with multiple inheritance

Let's say I want to create SomeClass, which inherits from two classes:
class SomeClass(InheritedClass1, InheritedClass2):
Both the InheritedClass1 and InheritedClass2 have the method with the same name, named performLogic.
If I declare super().peformLogic(), I will get result only from the first argument/inherited class. I need the results of both, so my question is, is there a way to call the method from the InheritedClass1 and then from the InheritedClass2 by using super()?
Thanks.
EDIT:
Class example which I need to 'solve' is constructed like this (simplified, and skipped non-essential methods for brevity):
class One:
...
def getOutput(self):
self.output = self.performLogic()
return self.output
class Two(One):
...
def getFirstValue(self):
return input()
def getSecondValue(self):
return input()
class Three(Two):
...
def performLogic(self):
(some logic performation based on inputs from class Two methods)
class Four(Two):
...
def performLogic(self):
(some *different* logic performation based on inputs from class Two methods)
What I need to do now is implement a class which will perform logic of both class Three as well as class Four but with only one pair of input values. So I declared:
class Five(Three,Four):
def performLogic(self):
*and here I got stuck*
*super().performLogic() will ask me for input values and returns the
*result of class Three's performLogic()*
*but what of class Four, I need the result of it's performLogic() with
*a single pair of input values, too?*
super is not a universal replacement for calling a method in a parent base class; it requires that classes be designed cooperatively. This means that every class needs to call super().performLogic, just in case it is not the last element of some class's MRO.
Ultimately, there has to be some class at the end of the method resolution order which cannot call super().peformLogic(), either because it is the last class on the list or the next call would be delegated to a class (like object) which does not define performLogic. In this case, you'll have to provide such a root class yourself.
class LogicPerformer:
def performLogic(self):
# No call to super; the buck stops here, because object
# doesn't have this method
print("In LogicPerformer")
class InheritedClass1(LogicPerformer):
def performLogic(self):
print("In InheritedClass1")
super().performLogic()
class InheritedClass2(LogicPerformer):
def performLogic(self):
print("In InheritedClass1")
super().performLogic()
class SomeClass(InheritedClass1, InheritedClass2):
def performLogic(self):
print("In SomeClass")
super().performLogic()
a = SomeClass()
print(SomeClass.__mro__)
a.performLogic()
This is actually a very interesting question. I think there would not be any features in the language to allow this. What you basically want to do is to use method resolution in the language to call two methods where method resolution would always resolve one method. Hence, this cannot be done. If you want to call two separate methods, you need to do it yourself explicitly.

Class factories and abstract base classes

I am trying to define a number of classes based on an abstract base class. Each of these classes basically defines a cell shape for a visualisation package. The cell is comprised of a number of vertices (points) and each subclass will require a different number of points. Each class can be thought of as a container for a fixed number of point coordinates.
As an example, consider the base class Shape, which is simply a container for a list of coordinates:
class Shape(object):
"""Cell shape base class."""
def __init__(self, sequence):
self.points = sequence
#property
def points(self):
return self._points
#points.setter
def points(self, sequence):
# Error checking goes here, e.g. check that `sequence` is a
# sequence of numeric values.
self._points = sequence
Ideally I want to be able to define, say, a Square class, where the points.setter method checks that sequence is of length four. Furthermore I would like a user to not be able to instantiate Shape. Is there a way I can define Shape to be an abstract base class? I have tried changing the definition of shape to the following:
import abc
class Shape(object):
"""Cell shape base class."""
__metaclass__ = abc.ABCMeta
def __init__(self, sequence):
self.points = sequence
#abc.abstractproperty
def npoints(self):
pass
#property
def points(self):
return self._points
#points.setter
def points(self, sequence):
# Error checking goes here...
if len(sequence) != self.npoints:
raise TypeError('Some descriptive error message!')
self._points = sequence
This requires subclasses to define the property npoints. I can then define a class Square as
class Square(Shape):
#property
def npoints(self):
return 4
However, this would be rather tedious to implement for a large number of sublcasses (and with more than one property to implement). I was hoping to define a class factory which would create my subclasses for me, something along the lines of:
def Factory(name, npoints):
return type(name, (Shape,), dict(npoints=npoints))
Triangle = Factory('Triangle', 3)
Square = Factory('Square', 4)
# etc...
Is this class factory function a valid approach to take, or am I clobbering the npoints property? Is it better to replace the call to type with something more verbose like:
def Factory(name, _npoints):
class cls(Shape):
#property
def npoints(self):
return _npoints
cls.__name__ = name
return cls
An alternative approach would be to define a class attribute _NPOINTS and change the npoints
property of Shape to
#property
def npoints(self):
return _NPOINTS
However, then I loose the benefit of using an abstract base class since:
I can't see how to define a class attribute using type, and
I don't know how to define an abstract class attribute.
Does anyone have any thoughts on the best way to implement this abstract base class and class factory function, or even an altogether better design?
Without knowing more about your project, I cannot give specific advice on the general design. I will just provide a few more general hints and thoughts.
Dynamically generated classes are often a sign that you don't need separate classes at all – simply write a single class that incorparates all the functionality. What's the problem with a Shape class that gets it's properties at instantiation time? (Of course there are reasons to use dynamically generated classes – the namedtuple() factory function is one example. I couldn't find any specific reasons in your question, however.)
Instead of using abstract base classes, you often simply document the intended interface, and than write classes conforming to this interface. Due to the dynamic nature of Python, you don't strictly need a common base class. There are often other advantages to a common base class – for example shared functionality.
Only check for application code errors if not doing so leads to strange errors in unrelated places. If, say, your function expects an iterable, simply assume you got an iterable. If the user passed in something else, you code will fail when it tries to iterate the passed in object anyway, and the error message will usually be enough for the application developer to understand the error.

Categories

Resources