How to avoid class inheritance [closed] - python

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I've recently been dealing a lot with class inheritance on a project I'm working on, and I've started to become disenchanted with it as a programming concept. I do understand its appeal: it provides a clean way to extend an existing base class with new methods, thereby avoiding having to rewrite the same code multiple times and adding a nice logical structure to how classes are related to one another.
However, now that I've been using it more extensively, its drawbacks have become much more apparent. Not only does it add a layer of opacity to where a method or attribute comes from, forcing me to go down a rabbit hole of inherited classes every time I want to figure out where a given method is being defined, but it also breaks encapsulation by allowing you to unwittingly redefine public and private functions and variables in an inherited class.
Here's a very simple example of how easy it is to break things with inheritance.
class Parent:
def __init__(self):
self._private_var = 10
def add_ten(self, n):
return n + self._private_var
class Child(Parent):
def __init__(self):
self._private_var = 100
def add_hundred(self, n):
return n + self._private_var
Now, let's say I want to use Child's inherited .add_ten method:
c = Child()
c.add_ten(4)
>> 104
Since I unknowingly redefined Parent's ._private_var, the .add_ten method now adds 100 instead of 10.
Granted, inheritance might be dealt with slightly differently in other languages (I know Python doesn't have any truly "private" methods or variables, so perhaps this is not as much of an issue in Java or C++). Still, the downsides of inheritance seem to me to outweigh its advantages and make we want to avoid using it altogether if I can.
The issue is that the alternative seems to add a lot of redundancy.
For example, I could have defined ChildTwo as:
class ChildTwo:
def __init__(self):
self._parent = Parent()
self._private_var = 100
def add_ten(self, n):
return self._parent.add_ten(n)
def add_hundred(self, n):
return n + self._private_var
This would allow both .add_ten and .add_hundred to behave as expected, but it would also require me to manually add every method I would like to inherit from the Parent class, which seems wasteful in terms of keeping my code lean. This is especially true when there are multiple methods I'd like to inherit from Parent.
I'm also not sure (?) if instantiating the Parent class for every ChildTwo class might have some impact on performance.
What's the best way to avoid using inheritance while still avoiding code repetition as much as possible and having a minimal impact on performance?
Edit: Someone pointed out that this is a bad example, since .add_ten should probably be defined as n + 10 instead of n + self._private_var. That's a fair point, but it requires that I know how Parent is implemented, which may not always be the case. If Parent is in some external module then there's nothing I can do about it. Furthermore, if its implementation of .add_ten changes in the future, it has an impact on the Child class as well.

There are obviously no hard rules on when and when not to use inheritance. However, there are a few key things I do to help avoid issues.
I treat child classes as just extensions of the parent's logic. I therefore try to avoid overwriting objects, instead only extending them.
For example, I commonly have a parent class which receives the configs for a project. Then, any child classes can use these configs and do whatever necessary logic with them. All the configs are the same, they're not being changed, so inheritance will not cause any issues.
class Parent:
def __init__(self, name, configs):
self.name = name
self.theory = configs['theory']
self.log_file = configs['log_file']
...
class Child(Parent):
def __init__(self, name, configs):
super().__init__(name, configs)
I would not however have a method in the parent class that performed some action with the configs and then alter that method in the child classes. Despite that being perfectly acceptable python code, I find it easy to make mistakes and it adds unnecessary complexity. Why bother writing a method if you're going to constantly override it?
With multiple inheritance, if it's not something you've encountered before, it can be surprisingly easy to run into issues with "Method Resolution Order". The Diamond of Death or whatever other dramatic names it has. This occurs when multiple inheritance leads to ambiguity in how a child class should inherit from above it in the inheritance tree. For this reason I completely avoid ever making classes "siblings".
Inheritance can often scale badly. By which I mean, adding lots of logic to a pre-existing inheritance structure can cause issues. Maybe your child classes all used the parent class method in the same way but now you've a new child class which is slightly different. Ok so you can overwrite that method. But what if you begin adding more and more child classes which also need to overwrite that method? Now it makes sense to rewrite the base class method which means you need to rewrite all of the overwritten methods.
Sometimes inheritance will be instrumental in reducing repetition, other times it will be a headache for maintenance, testing and extension. As always in programming, if you find yourself writing the same thing over and over, you're doing something wrong. Knowing exactly what a class structure will be used for in the future, for me has been the best way of making sure any inheritance won't cause issues.
I would just say that your example seems a bit of a straw-man. You set up a demonstrably bad structure then dismiss inheritance as the reason for failure. If you're going to add ten, add ten, don't add some changeable variable.
Finally, while I have banged on about personal preference, be aware in the working environment, people's preferences will be drastically different to yours. You should understand how to use, extend and debug all different class structures.

Related

How to redesign an instance where common functionality (in a parent class) warrants returning instances of the children?

As per the title, I know this is inherently terrible design, as a parent should know nothing about its children. However, I am in the scenario where
All children have reusable behaviour that could be derived from the parent class,
Those reusable methods that all subclasses need, warrant returning instances of the current sub classes!
I need all child classes to have the repeatable behaviour but also have the parent class capable of returning instances of the children, this will need a complete refactor, but how do I design it properly?
I have tried using composition instead here, the only problem being that all classes will have to have to declare an explicit public API for using the common functionality and each and every subclass in the future will need to declare it.
class BasePage(object):
# Some Very Common Behaviour goes here.
# Nothing product specific, just selenium specific
class ProductBasePage(BasePage):
# Reusable behaviour here for the product
# Some of this behaviour in the parent does web-app navigation
# which merits (quite rightly) to return instances of children
# pages, for example (CustomersPage, DashboardPage, ProductsPage)
# but this is clearly flawed and inheritance is not the answer?
# but how do I keep the functionality that all subclasses need
# without then declaring it explicitly in every one of them?
class CustomersPage(ProductBasePage):
# Customer specific behaviour
# Should have access the common functionality implicitly from the parent
Customer Page and all other pages which extend the product page should have the ability to use a lot of common functionality that all of the subclasses of ProductBasePage should have, without having each and every subclass to define that behaviour explicitly. The re-usable methods however, return instances of ProductBasePages sub classes as their actions warrant this behaviour.
So how do I achieve this? I think Inheritance is not the answer here, but then how do I get reusability of the common functionality without declaring it explicitly in every class?
The solution to this problem should avoid circular Python import dependencies as well.
Just put those reusable methods into a separated class, and make them static. Then the subclasses can call those methods implicitly.
If you don't want to use static methods, go with plain functions.

Python defining gui variables outside init [duplicate]

This question already has answers here:
Instance variables in methods outside the constructor (Python) -- why and how?
(7 answers)
Closed 4 years ago.
I'm using PySide2 to define my tool's interface, and I generally initialize all interface items outside __init__ as to not bloat it (any other important variables stay in __init__).
Unfortunately for me, I'm using PyCharm as my editor and it's giving me tons of warnings:
Instance attribute 'foobar' defined outside __init __
Here's a simple example of what I would be doing:
from PySide2 import QtWidgets
class MyTool(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyTool, self).__init__(parent)
self.create_gui()
def create_gui(self):
# Complains about all variables below!
self.awesome_checkbox = QtWidgets.QCheckBox(parent=self)
self.awesome_button = QtWidgets.QPushButton(parent=self)
self.awesome_label = QtWidgets.QLabel(parent=self)
self.main_layout = QtWidgets.QVBoxLayout()
self.main_layout.addWidget(self.awesome_checkbox)
self.main_layout.addWidget(self.awesome_button)
self.main_layout.addWidget(self.awesome_label)
self.setLayout(self.main_layout)
Now I know one solution would be to initialize these variables in __init__ as None, but I can have fairly complex interfaces so it would be very long winded.
My question is if what I'm currently doing truly blasphemy? I know the variables are technically outside __init__, but the method is being called in the constructor anyways!
Well, the short answer is: no, it's not "truly blasphemy".
It's considered good practice to create all the instance attribute and ensure they are in a consistant state in the initializer because it makes code easier to read (you only have one method to read to know what attributes your object has) and avoids potential AttributeError when an attribute is created by a method that might not always been called before the attribute is accessed. That's why most linters will (by default) warn you about this, and by itself it's a good thing as it can help you spot a potential bug before it makes it's way in production.
Now there are indeed cases where it makes sense to delegate part of the instance initialisation to a distinct method, ie when those attributes depends on each other and some other external factor and might have to be reset / updated together during the instance's lifecycle, or when you want to let child classes override this part of the initialization without having to override the __init__() method itself (cf the GOF's "template method" pattern).
In the case of a class with complex initialization (and this is typical of GUI components) it can also make sense to split the setup in distinct methods for readability reasons - a 50+ lines initializer is not really optimal when it comes to readability - so as far as I'm concerned I would probably do something similar with possibly a couple improvements: first make this a "protected" method (naming it _create_gui() - the leading underscore being the naming convention for protected attributes / methods) and then adding a guard to prevent the method from being executed twice (assuming this method is only supposed to be called once from the initializer and is not supposed to be part of the public API, of course). And then I would add a couple linter directives (those are specially formatted comments that the linter looks for) to make clear for both the linter and anyone reading this code that doing so was a deliberate design choice and not a rookie mistake.

Which strategy follow to create class?

I am trying to learn how classes work. I would like to create different classes with some shared elements and others not, but as far as I know I can create it from three different ways:
Create a class with all the shared elements and then inherit this class and modify specific methods and attributes in the new class. Something like that:
class Enemy(object):
"""Enemy!"""
def __init__(self, damage=30, life=100, enemy="Hero"):
#And keep defining the class and its methods and attributes in common with all the enemys
class Troll(Enemy):
"""Troll!"""
def __init__ (self, defense=0):
#And define the specific attributes and methods for Trolls.
Create a class and ask for a type of class, and change the definition of the object from which input it got. Something like that:
class Enemy(object):
"""Enemy!"""
def __init__(self, damage=30, defense=0, life=100, type="Troll" enemy="Hero"):
if type=="Troll":
#Do that for type "Troll"
if type=="Goblin":
#Do that for type "Goblin"
#And keep defining the class and its methods and attributes for each accepted type
Create two different classes and then do multiple inheritance:
class Enemy(object):
"""Enemy!"""
def __init__(self, damage=30, life=100, enemy="Hero"):
#And keep defining the class and its methods and attributes in common with all the enemys
class Trolls(object):
"""Trolls!"""
def __init__ (self, defense=1, shield=20):
#And define the specific attributes and methods for Trolls.
class SuperTroll(Enemy, Trolls):
I see the first one is the easy one that let me be more flexible to create multiple classes with shared methods and attributes. But the second one seems to me more easy to use(or at least I like it), and I can get out of the if conditionals whatever I want. And the third one could be practical for mixing different classes without any shared method or attribute, and if they share it wouldn't mess it (or it would?).
Which one is better?
But about writing in one or the other way it seems it is just a matter of strategy about how do you want your code. Is this correct?
The second example is not a good idea; it will lead to a lot of duplicated code, and you have to edit your Enemy class every time you come up with a new type of enemy character.
Picking between the first and third is trickier, and will depend on what you want to achieve ("a matter of strategy", as you have it).
The first, single inheritance from Enemy for Troll and Goblin, is useful because it allows you to define all the code that all Enemy characters will have once, and only define the differences in the Troll and Goblin classes. You could extend this further, and have Enemy inherit from a superclass (or metaclass) Character, that provides the really basic stuff for both Enemy and Hero classes (e.g. name, health, ...).
class Enemy(Character)
class Troll(Enemy)
The third example may be useful if you want to separate characters and roles, e.g. you could have
class FriendlyTroll(Troll, Friend)
and
class UnfriendlyTroll(Troll, Enemy)
if those roles are going to mean different instance attributes (e.g. the Friend mix-in might introduce a share method). This allows for more complexity in your character definitions, but if you are not going to use the extra functionality it is a lot of complication to get your head around, and may lead to intractable multiple-inheritance problems.
TL;DR: Use the first one! If you decide later that you really need to separate out roles into mix-in classes, that isn't too complex a task.
First one:
Proper way to go...You are inheriting from what is collectively "ENEMY". Here you can have common things that "ENEMY" has under the class and have individual classes for all types of enemy and also you can well override some of the methods that makes some kind of "ENEMY" distinct.
Second one:
Not nice or properly object oriented because what if "trolls" have some property that is not shared by other "enemy". You might say that it can go under "if" statement you have but this makes the code very-less manageable which is why you use OOP ( to manage the code, it's not all but one of the reason).
Third one:
I strongly suggest not using multiple-inheritance as far as possible. Google diamond death problem and you will know why. One rule for multiple-inheritence--"If you think you need multiple inheritance, you are probably wrong. If you know you have to use multiple inheritance, you are probably right."
Well, classes are what essentially describes differences in behavior of the objects of those classes. That is you may choose either approach but only after you sit down and decide:
in your program what is the difference between a given troll and an enemy in general. If there's such a difference (i.e. there're situations when "trolls" behave somewhat different from enemies in general, or have their own methods (actions) etc) then you need to differentiate class Troll and class Enemy. If all they differ is their name, then you probably don't need a separate class...
You need multiple inheritance (the 3rd case) only if you need to mix-in behaviors of two unrelated classes. Generally it's a hard way to go, with certain traps and pitfalls. But if you're clever and accurate enough then you may succeed with it.

Python: add a parent class to a class after initial evaluation

General Python Question
I'm importing a Python library (call it animals.py) with the following class structure:
class Animal(object): pass
class Rat(Animal): pass
class Bat(Animal): pass
class Cat(Animal): pass
...
I want to add a parent class (Pet) to each of the species classes (Rat, Bat, Cat, ...); however, I cannot change the actual source of the library I'm importing, so it has to be a run time change.
The following seems to work:
import animals
class Pet(object): pass
for klass in (animals.Rat, animals.Bat, animals.Cat, ...):
klass.__bases__ = (Pet,) + klass.__bases__
Is this the best way to inject a parent class into an inheritance tree in Python without making modification to the source definition of the class to be modified?
Motivating Circumstances
I'm trying to graft persistence onto the a large library that controls lab equipment. Messing with it is out of the question. I want to give ZODB's Persistent a try. I don't want to write the mixin/facade wrapper library because I'm dealing with 100+ classes and lots of imports in my application code that would need to be updated. I'm testing options by hacking on my entry point only: setting up the DB, patching as shown above (but pulling the species classes w/ introspection on the animals module instead of explicit listing) then closing out the DB as I exit.
Mea Culpa / Request
This is an intentionally general question. I'm interested in different approaches to injecting a parent and comments on the pros and cons of those approaches. I agree that this sort of runtime chicanery would make for really confusing code. If I settle on ZODB I'll do something explicit. For now, as a regular user of python, I'm curious about the general case.
Your method is pretty much how to do it dynamically. The real question is: What does this new parent class add? If you are trying to insert your own methods in a method chain that exists in the classes already there, and they were not written properly, you won't be able to; if you are adding original methods (e.g. an interface layer), then you could possibly just use functions instead.
I am one who embraces Python's dynamic nature, and would have no problem using the code you have presented. Make sure you have good unit tests in place (dynamic or not ;), and that modifying the inheritance tree actually lets you do what you need, and enjoy Python!
You should try really hard not to do this. It is strange, and will likely end in tears.
As #agf mentions, you can use Pet as a mixin. If you tell us more about why you want to insert a parent class, we can help you find a nicer solution.

Is anyone using meta-meta-classes / meta-meta-meta-classes in Python/ other languages?

I recently discovered metaclasses in python.
Basically a metaclass in python is a class that creates a class. There are many useful reasons why you would want to do this - any kind of class initialisation for example. Registering classes on factories, complex validation of attributes, altering how inheritance works, etc. All of this becomes not only possible but simple.
But in python, metaclasses are also plain classes. So, I started wondering if the abstraction could usefully go higher, and it seems to me that it can and that:
a metaclass corresponds to or implements a role in a pattern (as in GOF pattern languages).
a meta-metaclass is the pattern itself (if we allow it to create tuples of classes representing abstract roles, rather than just a single class)
a meta-meta-metaclass is a pattern factory, which corresponds to the GOF pattern groupings, e.g. Creational, Structural, Behavioural. A factory where you could describe a case of a certain type of problem and it would give you a set of classes that solved it.
a meta-meta-meta-metaclass (as far as I could go), is a pattern factory factory, a factory to which you could perhaps describe the type of your problem and it would give you a pattern factory to ask.
I have found some stuff about this online, but mostly not very useful. One problem is that different languages define metaclasses slightly differently.
Has anyone else used metaclasses like this in python/elsewhere, or seen this used in the wild, or thought about it? What are the analogues in other languages? E.g. in C++ how deep can the template recursion go?
I'd very much like to research it further.
This reminds me of the eternal quest some people seem to be on to make a "generic implementation of a pattern." Like a factory that can create any object (including another factory), or a general-purpose dependency injection framework that is far more complex to manage than simply writing code that actually does something.
I had to deal with people intent on abstraction to the point of navel-gazing when I was managing the Zend Framework project. I turned down a bunch of proposals to create components that didn't do anything, they were just magical implementations of GoF patterns, as though the pattern were a goal in itself, instead of a means to a goal.
There's a point of diminishing returns for abstraction. Some abstraction is great, but eventually you need to write code that does something useful.
Otherwise it's just turtles all the way down.
To answer your question: no.
Feel free to research it further.
Note, however, that you've conflated design patterns (which are just ideas) with code (which is an implementation.)
Good code often reflects a number of interlocking design patterns. There's no easy way for formalize this. The best you can do is a nice picture, well-written docstrings, and method names that reflect the various design patterns.
Also note that a meta-class is a class. That's a loop. There's no higher level of abstractions. At that point, it's just intent. The idea of meta-meta-class doesn't mean much -- it's a meta-class for meta-classes, which is silly but technically possible. It's all just a class, however.
Edit
"Are classes that create metaclasses really so silly? How does their utility suddenly run out?"
A class that creates a class is fine. That's pretty much it. The fact that the target class is a meta class or an abstract superclass or a concrete class doesn't matter. Metaclasses make classes. They might make other metaclasses, which is weird, but they're still just metaclasses making classes.
The utility "suddenly" runs out because there's no actual thing you need (or can even write) in a metaclass that makes another metaclass. It isn't that it "suddenly" becomes silly. It's that there's nothing useful there.
As I seed, feel free to research it. For example, actually write a metaclass that builds another metaclass. Have fun. There might be something useful there.
The point of OO is to write class definitions that model real-world entities. As such, a metaclass is sometimes handy to define cross-cutting aspects of several related classes. (It's a way to do some Aspect-Oriented Programming.) That's all a metaclass can really do; it's a place to hold a few functions, like __new__(), that aren't proper parts of the class itself.
During the History of Programming Languages conference in 2007, Simon Peyton Jones commented that Haskell allows meta programming using Type Classes, but that its really turtles all the way down. You can meta-meta-meta-meta etc program in Haskell, but that he's never heard of anyone using more than 3 levels of indirection.
Guy Steele pointed out that its the same thing in Lisp and Scheme. You can do meta-programming using backticks and evals (you can think of a backtick as a Python lambda, kinda), but he's never seen more than 3 backticks used.
Presumably they have seen more code than you or I ever has, so its only a slight exaggeration to say that no-one has ever gone beyond 3 levels of meta.
If you think about it, most people don't ever use meta-programming, and two levels is pretty hard to wrap your head around. I would guess that three is nearly impossible, and the that last guy to try four ended up in an asylum.
Since when I first understood metaclasses in Python, I kept wondering "what could be done with a meta-meta class?". This is at least 10 years ago - and now, just a couple months ago, it became clear for me that there is one mechanism in Python class creation that actually involves a "meta-meta" class. And therefore, it is possible to try to imagine some use for that.
To recap object instantiation in Python: Whenever one instantiates an object in Python by "calling" its class with the same syntax used for calling an ordinary function, the class's __new__ and __init__. What "orchestrates" the calling of these methods on the class is exactly the class'metaclass' __call__ method. Usually when one writes a metaclass in Python, either the __new__ or __init__ method of the metaclass is customized.
So, it turns out that by writing a "meta-meta" class one can customize its __call__ method and thus control which parameters are passed and to the metaclass's __new__ and __init__ methods, and if some other code is to be called before of after those. What turns out in the end is that metcalsses themselves are usually hardcoded and one needs just a few, if any, even in very large projects. So any customization that might be done at the "meta meta" call is usually done directly on the metaclass itself.
And them, there are those other less frequent uses for Python metaclasses - one can customize an __add__ method in a metaclass so that the classes they define are "addable", and create a derived class having the two added classes as superclasses. That mechanism is perfectly valid with metaclasses as well - therefore, so just we "have some actual code", follows an example of "meta-meta" class that allows one to compose "metaclasses" for a class just by adding them on class declaration:
class MM(type):
def __add__(cls, other):
metacls = cls.__class__
return metacls(cls.__name__ + other.__name__, (cls, other), {})
class M1(type, metaclass=MM):
def __new__(metacls, name, bases, namespace):
namespace["M1"] = "here"
print("At M1 creation")
return super().__new__(metacls, name, bases, namespace)
class M2(type, metaclass=MM):
def __new__(metacls, name, bases, namespace):
namespace["M2"] = "there"
print("At M2 creation")
return super().__new__(metacls, name, bases, namespace)
And we can see that working on the interactive console:
In [22]: class Base(metaclass = M1 + M2):
...: pass
...:
At M1 creation
At M2 creation
Note that as different metaclasses in Python are usually difficult to combine, this can actually be useful by allowing a user-made metaclass to be combined with a library's or stdlib one, without this one having to be explicitly declared as parent of the former:
In [23]: import abc
In [24]: class Combined(metaclass=M1 + abc.ABCMeta):
...: pass
...:
At M1 creation
The class system in Smalltalk is an interesting one to study. In Smalltalk, everything is an object and every object has a class. This doesn't imply that the hierarchy goes to infinity. If I remember correctly, it goes something like:
5 -> Integer -> Integer class -> Metaclass -> Metaclass class -> Metaclass -> ... (it loops)
Where '->' denotes "is an instance of".

Categories

Resources