I have a number of Python classes, Say, Class1
, Class2
, Class3
etc from a library/package. I want to extend all of the classes with some common functionalities. If I extend each class individually, we introduce a lot of redundancy and break the "Don't Repeat Yourself" acronym. So, my thought is to have a Base
class and use it extend other classes. For example:
class Base:
def __init__(self):
# I want self.base_attr_1, self.base_attr_2 and so on...
def base_method_1(self, *args, **kwargs):
pass
def base_method_2(self, *args, **kwargs):
pass
# and so on...
So we can extend Class1
, Class2
and so on using maybe Multiple inheritances. Say,
class Class1(Class1, Base):
pass
class Class2(Class2, Base):
pass
# and so on...
So that at last when I create an object of Class1
, Class2
etc., I can use the Base
class attributes and methods. Like;
class_1 = Class1(*args, **kwargs)
print(class_1.base_attr_1)
print(class_1.base_attr_2)
class_1.base_method_1(*args, **kwargs)
class_2.base_method_2(*args, **kwargs)
# and so on..
Please explain how to implement the Class1
, Class2
etc, to extend the Base
class.
Any help is highly appreciated. Thank you.
CodePudding user response:
following your description, you would have two possibilities to handle your issue:
- metaclass
- decorator
If I was you, I would try something like this (decorator solution) :
from functools import wraps
def deco(cls):
def test(x):
return x**2
d = {k:v for k,v in locals().items() if k != "cls"}
@wraps(cls)
def wrapper(*args, **kwargs):
o = cls(*args, **kwargs)
#o.test = test # setattr(o,"test", test) will be better solution,
# if you have more elements, which you'd like to add
# generalized :
# ============
for k,v in d.items(): setattr(o,k,v)
return o
return wrapper
@deco
class A(object):
pass
a = A()
print(a.__dict__)
print(a.test(10))
Result:
{'test': <function test at 0x02843C70>}
100
CodePudding user response:
When you have an inheritance setup like this:
class MyClass1(Base, ExternalClass1):
Calling super().__init__()
will call Base.__init__()
(first inherited class probably) and hence only initialize attributes in Base
.
You can explicitly call the init functions of all ancestors on the same inheritance "level" like this:
class MyClass1(Base, ExternalClass1):
def __init__(self) -> None:
Base.__init__(self)
ExternalClass1.__init__(self)
That will initialize attributes from both Base
and ExternalClass1
.
Example:
class ExternalClass1():
def __init__(self) -> None:
self.external_attr_1 = "external attr"
def external_func1(self):
print("external_func1")
class Base():
def __init__(self) -> None:
self.base_attr_1 = "base attr"
def base_method_1(self):
print("base_method_1")
class MyClass1(Base, ExternalClass1):
def __init__(self) -> None:
Base.__init__(self)
ExternalClass1.__init__(self)
self.my_attr_1 = "my class attr"
self.external_func1()
self.base_method_1()
self.my_class_1_func()
print(self.external_attr_1)
print(self.base_attr_1)
print(self.my_attr_1)
def my_class_1_func(self):
print("my_class_1_func")
cl1 = MyClass1()
Outputs:
external_func1
base_method_1
my_class_1_func
external attr
base attr
my class attr
Note however that self
is now shared between all 3 classes, which can cause clashes. Another pattern is of course to go for composition, and have the external class be a property of MyClass1
. This is both safer and introduces less coupling.