I have a class that extends a base class. Upon instantiation, I want to check if the subclass has one of the classes implemented from its base, but I'm not sure the best way. hasattr(self, '[method]')
returns the method from super if not implemented by the child, so I'm trying to tell the difference.
Here is an example:
class Base :
def __init__ ( self,) :
pass
def fail (self,) :
pass
# Now create the subclass w/o .fail
class Task ( Base ) :
def __init__ ( self, ):
print( hasattr( self, 'fail' ) ) # < returns True
When Task()
is instantiated it prints True
because Task
inherits .fail
from Base
. But in this case, I want to know that Task
Does Not implement .fail
, so I want a False
returned somehow. It seems like I'm looking for something like isimplemented( self, 'fail' )
. What am I missing?
CodePudding user response:
IIUC, you can check super().fail == self.fail
class Base:
def __init__(self):
pass
def fail(self):
pass
class Task(Base):
def __init__(self):
print(super().fail == self.fail)
class Task2(Base):
def __init__(self):
print(super().fail == self.fail)
def fail(self):
# Override
pass
Output:
t1 = Task()
# True
t2 = Task2()
# False
CodePudding user response:
Not sure if I understand correctly but you could check if fail
method is in the vars
of the classes, but not inherited to the main class.
So you could try:
class Base:
def __init__(self):
print(self.__dir__())
def fail(self):
pass
class Task(Base):
def __init__(self):
print('fail' not in vars(Task))
class Task2(Base):
def __init__(self):
print('fail' not in vars(Task2))
def fail(self):
# Override
pass
t1 = Task()
t2 = Task2()
Output:
True
False
Or use __dict__
:
...
class Task(Base):
def __init__(self):
print('fail' not in Task.__dict__)
class Task2(Base):
def __init__(self):
print('fail' not in Task2.__dict__)
def fail(self):
# Override
pass
...
CodePudding user response:
I'm not sure I understand correctly, but it sounds like you might be looking for Abstract Base Classes. (Documentation here, tutorial here.) If you specify an abstractmethod
in a base class that inherits from abc.ABC
, then attempting to instantiate a subclass will fail unless that subclass overrides the abstractmethod.
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def fail(self):
pass
class Task(Base):
pass
class Task2(Base):
def fail(self):
pass
# this raises an exception
# `fail` method has not been overridden in the subclass.
t1 = Task()
# this succeeds
# `fail` method has been overridden in the subclass.
t2 = Task2()
If you want a check to happen at class definition time rather than instance instantiation time, another option is to write an __init_subclass__
method in your base class, which is called every time you subclass your base class or you subclass a class inheriting from your base class. (You don't have to raise an exception in __init_subclass__
— you could just add a fail_overriden
boolean attribute to the class, or do anything you like really.)
class Base:
def fail(self):
pass
def __init_subclass__(cls, **kwargs):
if cls.fail == Base.fail:
raise TypeError(
'Subclasses of `Base` must override the `fail` method'
)
super().__init_subclass__(**kwargs)
# this class definition raises an exception
# because `fail` has not been overridden
class Task(Base):
pass
# this class definition works fine.
class Task2(Base):
def fail(self):
pass
And if you just want each instance to tell you whether fail
was overridden in their subclass, you can do this:
class Base:
def __init__(self):
print(type(self).fail != Base.fail)
def fail(self):
pass
class Task(Base):
def __init__(self):
super().__init__()
class Task2(Base):
def __init__(self):
super().__init__()
def fail(self):
pass
t1 = Task() # prints "True"
t2 = Task2() # prints "False"