How to avoid duplicate code if two classes extending different classes - python

I have two classes which extend two different base classes. Those classes setup custom environment.
The are three methods common in both the classes: prepare(), setup() and teardown().
Each method overrides base class method. (Also they have super() calls.) Also ExtendedBase extends Base.
First class:
ClassA(Base):
def __init__(self, args):
super().__init__(args)
self._private = OtherClass()
def prepare(self, args):
super().prepare(args)
self._private.prepare()
def setup(self, args):
super().setup(args)
self._private.setup(self._smth, args)
def teardown(self):
self._private.teardown()
super().teardown()
Second class:
ClassB(ExtendedBase):
def __init__(self, args):
super().__init__(args)
self._private = OtherClass()
def prepare(self, args):
super().prepare(args)
self._private.prepare()
def setup(self, args):
super().setup(args)
self._private.setup(self._smth, args)
def teardown(self):
self._private.teardown()
super().teardown()
Is this a way to avoid duplicate methods?
I thought about multiple inheritance with the Environment class which will contain duplicated methods, but am puzzled about how to implement this and if it is a good idea at all.
Edited: Unfortunately I couldn't do anything with classes hierarchy. ClassA and ClassB should inherit corresponding classes and override or use the parent's method.

This look a lot like a typical case to use class mixin:
class BuildMixin:
def __init_(self, args):
super().__init__(args)
self._private = OtherClass()
def prepare(self, args):
super().prepare(args)
self._private.prepare()
def setup(self, args):
super().setup(args)
self._private.setup(self._smth, args)
def teardown(self):
self._private.teardown()
super().teardown()
class ClassA(BuildMixin, Base):
pass
class ClassB(BuildMixin, ExtendedBase):
pass
For illustration purposes here are a bunch of dummy classes that follow your example and use mixin. From your question it is not clear what OtherClass should be, but it seems it also has the prepare, teardown and setup methods:
class OtherClass:
def prepare(self,arg):
print('OC perparing')
def teardown(self):
print('OC tearing down')
def setup(self, smth, args):
print('OC setting up')
class Base:
def __init__(self, args):
print('Base init')
self._smth=args
def prepare(self,arg):
print('Base perparing')
def teardown(self,):
print('base tearing down')
def setup(self,args):
print('base setting up')
class ExtendedBase:
def __init__(self, args):
print('ExtBase init')
self._smth=args
def prepare(self, arg):
print('ExtBase perparing')
def teardown(self):
print('ExtBase tearing down')
def setup(self, arg):
print('ExtBase setting up')
class BuildMixin:
def __init__(self, arg):
super().__init__(arg)
self._private = OtherClass()
def prepare(self, args):
super().prepare(args)
self._private.prepare(args)
def setup(self, args):
super().setup(args)
self._private.setup(self._smth, args)
def teardown(self):
self._private.teardown()
super().teardown()
class ClassA(BuildMixin, Base):
pass
class ClassB(BuildMixin, ExtendedBase):
pass
a = ClassA(1)
# prints:
# Base init
b = ClassB(1)
# prints:
# ExtBase init
a.prepare(1)
# prints:
# Base perparing
# OC perparing
b.prepare(1)
# prints:
# ExtBase perparing
# OC perparing
# and so on...

Related

How to inherit all class attributes, but one? Python

How to inherit all class 'A' attributes and methods, but 'b()'?
class A:
def __init__(self):
# attributes
pass
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def b(self):
# nothing
pass
do not use this old method( if there is another way to do it ):
class B(A):
def __init__(self, attributes):
super().__init__(self, attributes)
You can reimplement b() to raise an error:
class A:
def __init__(self):
# attributes
pass
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
#classmethod
def b(cls):
raise TypeError("method b is not supported in class B")
Also, if b() is a classmethod, you should probably override it as a classmethod.
Put that method in a separate class and don't inherit it.
class A:
def __init__(self):
# attributes
pass
class A1:
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Use multiple inheritance when you want that method.
class C(A,A1):
pass

How to subclass an Abstract Base Class?

Let's say I have an ABC:
class Template_ABC(metaclass=abc.ABCMeta):
def __init__(self, data=None, model=None):
self._data = data
self._model = model
#abc.abstractmethod
def do_stuff(self):
pass
#abc.abstractmethod
def do_more_stuff(self):
pass
I normally have a class of ABC, for example:
class Example_of_ABC(Template_ABC):
def do_stuff(self):
# Do stuff here
def do_more_stuff(self):
pass
Now, I want to subclass Example-of_ABC class. The only way I can do is as as follows:
class Subclass_of_Example_of_ABC(Example_of_ABC):
def __init__(self, data=None, model=None):
super().__init__(data, model)
def do_more_stuff(self):
# Do more stuff here
The issue with this way, is that I have to update my def _init__() for every subclass of the ABC. Is there anyway for the subclass to inherit all the inits from the ABC?

How to implement instance-dependent functionality using parent class decorator?

I would like to implement a parent class decorator in my child class whose functionality depends on the state of the child class instance. I've tried coming at this problem from three different angles, none of which have worked:
Parent method
If give_feedback is a static method, there's no self within the method. But if it's an instance method, there's no self within the namespace in which it's applied.
class Interface:
def __init__(self, quiet=False):
self.quiet = quiet
def echo(self, text):
if not self.quiet:
print(text)
def give_feedback(self, func):
def wrapper(*args):
print('Calling give_feedback.')
self.echo(func(*args))
return wrapper
class App(Interface):
#Interface.give_feedback # self not defined here.
def app_func(self, num):
feedback = 'Success with {num}'.format(num=num)
return feedback
if __name__ == '__main__':
a = App()
a.app_func(3)
Parent class using __call__ (cf. link example_1)
Can't access the object from within __call__.
class Interface:
# ...
class give_feedback:
def __init__(self, func):
self.func = func
def __call__(self, *args):
print(
'Calling {func}'.format(func=self.func)
)
instance = get_obj_instance(self.func) # What is this?
return instance.echo(self.func(instance, *args))
class App(Interface):
# ...
if __name__ == '__main__':
# ...
Parent descriptor (cf. link example_2)
Can access the object, but no arguments.
class Interface:
# ...
class give_feedback:
# ...
def __get__(self, instance, owner):
print(
'Getting {func} from {inst} of {ownr}'.format(
func=self.func, inst=instance, ownr=owner
)
)
num = 2 # How to get num???
return instance.echo(self.func(instance, num))
class App(Interface):
# ...
if __name__ == '__main__':
a = App()
a.app_func # No ability to pass parameters.
Is there a good way to do this?
Why not combine the 2nd and 3rd methods? Use __get__ to get the class instance, and __call__ to decorate with echo. Rather than returning app_func, return a new object that holds the instance and has the desired __call__ behavior.
class Interface:
def __init__(self, quiet=False):
self.quiet = quiet
def echo(self, text):
if not self.quiet:
print(text)
class give_feedback:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.InstHolder(instance, self.func)
class InstHolder:
def __init__(self, inst, func):
self.inst = inst
self.func = func
def __call__(self, *args):
return self.inst.echo(self.func(self.inst, *args))
class App(Interface):
#Interface.give_feedback
def app_func(self, num):
feedback = 'Success with {num}'.format(num=num)
return feedback
if __name__ == '__main__':
a = App()
a.app_func(3)
a.quiet = True
a.app_func(4)

Parent method being overwritten by child

Let's say we have:
class Parent():
def __init__(self):
foo()
def foo(self):
//do stuff
class Child(Parent):
def __init__(self):
Parent.__init__()
class Grandchild(Child):
def __init__(self):
Child.__init__()
def foo(self):
//different stuff
There are a lot of classes at the Child level that use the same foo(). Grandchild level has a slightly different version of foo, but when Grandchild is initiated, the foo() call in Parent.__init__() uses the Parent.foo() instead of Grandchild.foo().
Is there a correct practice when in comes to this kind of situation?
You're not calling the base classes' __init__() methods properly—you need to pass along the self argument to them:
class Parent:
def __init__(self):
self.foo()
def foo(self):
print('Parent stuff')
class Child(Parent):
def __init__(self):
Parent.__init__(self)
class Grandchild(Child):
def __init__(self):
Child.__init__(self)
def foo(self):
print('Grandchild stuff')
if __name__ == '__main__':
gc = Grandchild() # -> Grandchild stuff
If you use super() instead of explicitly stating the base class, you don't have to do that:
class Parent:
def __init__(self):
self.foo()
def foo(self):
print('Parent stuff')
class Child(Parent):
def __init__(self):
# Parent.__init__(self)
super().__init__()
class Grandchild(Child):
def __init__(self):
# Child.__init__(self)
super().__init__()
def foo(self):
print('Grandchild stuff')
if __name__ == '__main__':
gc = Grandchild() # -> Grandchild stuff
Another advantage is that you likely wouldn't have to change the code in a subclass' __init__() method if you changed its base class.

Is it ok to have a property decorated method to return a class in Python?

Let's say I have the following code:
class MyClass(object):
def __init__(self, param):
self.param = param
#property
def super_param(self):
return Param(self, self.param)
class Param(object):
def __init__(self, parent, param):
self.param = param
self.parent = parent
#property
def get_parent(self):
return self.parent
My question is, is it considered bad practice to use the #property decorator in this way? Are there any pros or cons?
Why not just
class MyClass(object):
def __init__(self, param):
self.param = Param(self, param)
I don't think there's anything particularly wrong with returning a class from a property; a better question is, what are you trying to accomplish by doing so?
Edit: if you don't want it changed, make it a private property:
class MyClass(object):
def __init__(self, param):
self._param = param # naming convention, 'don't muck with it'
# OR
self.__param = param # name mangled
#property
def param(self):
return self._param

Categories

Resources