Extending a class hierarchy in Python - python

I have a class hierarchy in a module that I want to extend.
The module to be extended looks something like this.
Module foo:
class bar(object): pass
class spam(bar): pass
class eggs(bar): pass
Now I want to extend these classes:
class my_bar(foo.bar):
def new_func(): pass
class my_spam(foo.spam): pass
class my_eggs(foo.eggs): pass
Doing so, a new function new_func() in my_bar would not be available in a my_spam instance using
my_spam_instance.new_func()
What is the best ("most pythonic") way to achieve this? I thought of multiple inheritance, like this:
class my_bar(foo.bar): pass
class my_spam(foo.bar, my_bar): pass
class my_eggs(foo.eggs, my_bar): pass
Though I never really used it before and I am not sure this is the best way.

You don't even need to inherit my_bar from bar
pythonic will be adding Mixin, which is actually a base class, but not inherited
class NewFuncMixin():
def new_func(): pass
And add it to new classes
class my_bar(foo.bar, NewFuncMixin): pass
class my_spam(foo.spam, NewFuncMixin): pass
class my_eggs(foo.eggs, NewFuncMixin): pass

What about a mixin class? The pattern is
class My_mixin( object):
def new_func(self): pass
class My_spam( My_mixin, foo.spam): pass
class My_eggs( My_mixin, foo.eggs): pass
mixins should inherit from object, and go on the left of the inheritance list so that the mixin class methods get name priority. Within the mixin you can then wrap any method of the superclass:
class My_mixin( object):
def bar_method( self):
# stuff
bar_result = super( My_mixin, self).bar_method()
# more stuff
return bar_result # or my_result based on bar_result
You can of course completely override the method instead of wrapping it.

Related

Python - empty class constructor (style)

What is a "Pythonic" way to define a class that does not need any constructor parameters?
class MyClass:
# class body
or do we need an explicit constructor? i.e.
class MyClass:
def __init__:
pass
# class body
Your first approach is good enough, unless you want to only use class attributes and not instance attributes
Also every class in Python inherits from object, as explained in detail here
class MyClass:
pass
An example of such classes might be as follows
A class to store multiple class attributes
class MyClass:
a = 1
b = 2
print(MyClass.a)
#1
print(MyClass.b)
#2
A custom exception where the constructor is implicitly taken from the base class
class MyException(Exception):
pass
raise MyException
# raise MyException
#__main__.MyException

Multilevel abstraction with interface and inheritance in Python

I'm not exactly sure how to phrase this question, hence the strange title. I also have not been able to find any information on this after searching, so hopefully this isn't a duplicate and I'm just searching for the wrong words. Anyhow, here is the situation, I have an abstract base class with some methods in it, which is inherited by a class. I don't want to set one of the methods in this base class, as this class is meant to be inherited by other classes to provide the common functionality they all share. Something like:
class A(metaclass=abc.ABCMeta):
#abc.abstractmethod
def fun1(self):
pass
#abc.abstractmethod
def fun2(self):
pass
class B(A):
def fun1(self):
#do work here
#abc.abstractmethod
def fun2(self): # Intent to have the final classes define this
pass
class C(B):
def fun2(self):
# do work here
class D(B):
def fun2(self):
# do work here
I would like to keep the function as an ABC.meta to force implementation on the final children, but because there can be multiple types of class B in this case all inheriting from the interface, I want to keep the initial virtulization of the method at this root class, but have a way for class B to enforce that it's sub-classes must implement this. The code works just find if I don't add the abstract method to class B, but that is awkward since subclassess must implement the method and shouldn't have to look all the way up to the interface to figure out everything they need to implement. As written, it will error out because class B cannot declare the method as an abc.abstract. If I don't declare it as an abstract there is no way to enforce the child class has to implement the method.
I hope my convoluted way of writing this makes sense to someone out there...
Thanks!
You probably should not redefine fun2 as an abstract method in the concrete class B. You are creating a set of rules for your interface, but immediately violating them when you do that.
Instead, either define a mix-in class or an additional ABC that C and D can inherit.
class A(metaclass=abc.ABCMeta):
#abc.abstractmethod
def fun1(self):
pass
class A2(metaclass=abc.ABCMeta):
#abc.abstractmethod
def fun2(self):
pass
class B(A):
def fun1(self):
print('hello')
class B2(A2):
def fun2(self):
print('world')
class C(B, B2):
pass
class D(B, B2):
pass

Python mixin with dynamic method names

Supposing I have the following classes:
class SomeMixin:
def apply(self):
pass
class Foo(SomeMixin):
pass
class Bar(SomeMixin):
pass
How do I implement SomeMixin so that the attached method apply becomes apply_foo in Foo and apply_bar in Bar? The methods in the mixin follows a generic algorithm, but the generated names matter to the framework I'm using.
You wouldn't do that in the mixin, but in the concrete classes themselves. Just define the methods so that they call apply directly.
class Foo(SomeMixin):
def apply_foo(self):
return self.apply()
class Bar(SomeMixin):
def apply_bar(self):
return self.apply()

Robot Framework keywords and Inheritance

I have a library of keywords. I have a few classes and subclasses, but I'm having an issue with inheritance and keywords being double-defined. For example:
MyLib.py
class Class1:
def __init__(self):
pass
def do_something_generic(self):
#do stuff that is generic to all subclasses
pass
class Subclass1(Class1):
def __init__(self):
pass
def do_something_specific_to_subclass1(self):
#something specific
pass
class Subclass2(Class1):
def __init__(self):
pass
def do_something_specific_to_subclass2(self):
#something specific
pass
The specific keywords work fine, but when I try to call Do Something Generic I get Multiple keywords with name 'Do Something Generic' found. I can fully qualify the library name with MyLib.Class1.Do Something Generic, but is there any way to define Do Something Generic to always refer to the superclass, since the method is only defined there and is simply inherited by the subclasses?
From Robot Framework User Guide
When the static library API is used, Robot Framework uses reflection to find out what public methods the library class or module implements.
It will exclude all methods starting with an underscore,
and with Java libraries also methods that are implemented only in java.lang.Object are ignored.
All the methods that are not ignored are considered keywords.
Have you considered adding helper base class with a _do_something_generic function? You can exclude it from __all__ list. Then use the inheritance to expose keywords from the base class in Class1.
MyLibrary.py:
__all__ = ['Class1', 'Subclass1', 'Subclass2']
class BaseClass:
def _do_something_generic(self):
pass
class Class1(BaseClass):
def __init__(self):
pass
def do_something_generic(self):
return self._do_something_generic()
class Subclass1(BaseClass):
def __init__(self):
pass
def do_something_specific_to_subclass1(self):
a = self._do_something_generic()
return (a, 3)
class Subclass2(BaseClass):
def __init__(self):
pass
def do_something_specific_to_subclass2(self):
#something specific
pass
I think the best solution is to simply move do_something_generic to a separate class, so that your base class only has helper functions and no public keywords:
class Class1:
def __init__(self):
pass
class Subclass0(Class1):
def do_something_generic(self):
#do stuff that is generic to all subclasses
pass
While it might be possible to solve this with something exotic like using __slots__ or __getattr__, or modifying self.__dict__, it's probably not worth the trouble.

How to change baseclass

I have a class which is derived from a base class, and have many many lines of code
e.g.
class AutoComplete(TextCtrl):
.....
What I want to do is change the baseclass so that it works like
class AutoComplete(PriceCtrl):
.....
I have use for both type of AutoCompletes and may be would like to add more base classes, so how can I do it dynamically?
Composition would have been a solution, but I do not want to modify code a lot.
any simple solutions?
You could have a factory for your classes:
def completefactory(baseclass):
class AutoComplete(baseclass):
pass
return AutoComplete
And then use:
TextAutoComplete = completefactory(TextCtrl)
PriceAutoComplete = completefactory(PriceCtrl)
On the other hand depending on what you want to achieve and how your classes look, maybe AutoComplete is meant to be a mixin, so that you would define TextAutoComplete with:
class TextAutocomplete(TextCtrl, AutoComplete):
pass
You could use multiple inheritance for this:
class AutoCompleteBase(object):
# code for your class
# remember to call base implementation with super:
# super(AutoCompleteBase, self).some_method()
class TextAutoComplete(AutoCompleteBase, TextCtrl):
pass
class PriceAutoComplete(AutoCompleteBase, PriceCtrl):
pass
Also, there's the option of a metaclass:
class BasesToSeparateClassesMeta(type):
"""Metaclass to create a separate childclass for each base.
NB: doesn't create a class but a list of classes."""
def __new__(self, name, bases, dct):
classes = []
for base in bases:
cls = type.__new__(self, name, (base,), dct)
# Need to init explicitly because not returning a class
type.__init__(cls, name, (base,), dct)
classes.append(cls)
return classes
class autocompletes(TextCtrl, PriceCtrl):
__metaclass__ = BasesToSeparateClassesMeta
# Rest of the code
TextAutoComplete, PriceAutoComplete = autocompletes
But I'd still suggest the class factory approach already suggested, one level of indentation really isn't that big of a deal.
You could modify the __bases__ tuple. For example you could add another baseclass:
AutoComplete.__bases__ += (PriceCtrl,)
But in general I would try to avoid such hacks, it quickly creates a terrible mess.

Categories

Resources