I had a look around but couldn't find any answers. I have a slight issue - I have an abstract base class with a few abstract methods but also with several methods that are generic to all subclasses. Yet, in order to use these methods, I need to pass an subclass-specific attribute. This works fine, but I, of course, get warnings that the base class doesn't have the specific attribute:
Unresolved attribute reference 'c' for class 'Foo'
Let's assume I have this code:
from abc import ABC
class Foo(ABC):
def __init__(self, a, b):
self.a = a
self.b = b
def do_stuff(self):
if hasattr(self, 'c'):
return self.a * self.c
elif hasattr(self, 'd'):
return self.a + self.d
class Bar(Foo):
def __init__(self, a, b, c):
super().__init__(a=a, b=b)
self.a = a
self.b = b
self.c = c
self.some_dict = {}
def get_value_from_dict(self):
return self.some_dict[self.d]
class Baz(Foo):
def __init__(self, a, b, d):
super().__init__(a=a, b=b)
self.a = a
self.b = b
self.d = d
So, Foo
is an abstract base class so it's never going to be called by itself but of course it's not nice to have these warnings. Yet if I add attribute c
to the base class with a value of None
, this results in an error because when subclass calls superclass' init, the value gets overwritten:
class Foo(ABC):
def __init__(self, a, b):
self.a = a
self.b = b
self.c = None
If I change base class' init as shown above and then instantiate class Bar
and call get_value_from_dict()
I will get a KeyError
, otherwise if I keep things as in the original example, then all works fine:
b = Bar(1, 2, 3)
b.do_stuff()
b.get_value_from_dict()
EDIT:
This is the actual code that I'm working with. This is what the do_stuff
method in my example was meant to represent. Here self.component
is a subclass-specific attribute and this generic method replaces erroneous values with a placeholder value.
There are several other generic methods in the base class that use self.component
in a similar fashion.
class VariableImputer(ABC):
def __init__(self, data: pd.DataFrame, deposit: str, output_loc: Optional[str] = None) -> None:
self.data = data
self.deposit = deposit
self.output_loc = output_loc
self.err_index: np.ndarray = np.full(self.data.shape[0], True)
def _replace_erroneous_values(self):
"""
Replace calculated component values with -99 for all rows indices of
which are in self.err_index.
"""
err_data = np.where(~self.err_index)[0]
self.data.loc[err_data, self.component] = -99
class PopulateValue(VariableImputer):
def __init__(self, data: pd.DataFrame, deposit: str, output_loc: Optional[str] = None):
super().__init__(data=data, deposit=deposit, output_loc=output_loc)
self.data = data
self.deposit = deposit
self.output_loc = output_loc
self.component = ['porosity', 'sg']
But warnings are still there. What is the proper way to handle this situation?
if you don't have some attr
in Foo
. you still try to call it. the IDE will warn it.
there are two options:
attr
as abstract attr.pytho3 below
from abc import ABC, abstractmethod
class Foo(ABC):
@property
@abstractmethod
def c(self):
pass