Home > database >  Determine if subclass has a base class's method implemented in Python
Determine if subclass has a base class's method implemented in Python

Time:09-18

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"
  • Related