【问题标题】:Determine if subclass has a base class's method implemented in Python确定子类是否具有在 Python 中实现的基类方法
【发布时间】:2021-11-10 03:11:45
【问题描述】:

我有一个扩展基类的类。实例化后,我想检查子类是否具有从其基础实现的类之一,但我不确定最好的方法。 hasattr(self, '[method]') 如果子进程没有实现,则返回 super 的方法,所以我试图区分。

这是一个例子:

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

Task() 被实例化时,它会打印True,因为TaskBase 继承.fail。但在这种情况下,我想知道Task 实现.fail,所以我想以某种方式返回False。好像我正在寻找类似isimplemented( self, 'fail' ) 的东西。我错过了什么?

【问题讨论】:

    标签: python python-3.x oop inheritance superclass


    【解决方案1】:

    IIUC,你可以查看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
    

    输出:

    t1 = Task()
    # True
    
    t2 = Task2()
    # False
    

    【讨论】:

    • 这是有道理的,除了在我的用例中,我希望t1 = Task()也返回False。因为我不想明确定义fail,如果它只会有一个pass...
    • 所以你想检查子类是否覆盖了它的超类的方法?也许你可以这样做super().fail != self.fail
    【解决方案2】:

    不确定我是否理解正确,但您可以检查fail 方法是否在类的vars 中,但未继承到主类。

    所以你可以试试:

    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()
    

    输出:

    True
    False
    

    或使用__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
    ...
    

    【讨论】:

      【解决方案3】:

      我不确定我是否理解正确,但听起来您可能正在寻找抽象基类。 (文档here,教程here。)如果您在继承自abc.ABC 的基类中指定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()
      

      如果您希望在类定义时间而不是实例实例化时间进行检查,另一种选择是在您的基类中编写一个__init_subclass__ 方法,该方法每次您对基类进行子类化或对从基类继承的类进行子类化时都会调用。 (您不必在 __init_subclass__ 中引发异常——您可以在类中添加一个 fail_overriden 布尔属性,或者做任何您真正喜欢的事情。)

      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
      

      如果您只想让每个实例告诉您fail 是否在其子类中被覆盖,您可以这样做:

      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"
      

      【讨论】:

      • 嗨,Alex,感谢您的精彩帖子。选项 1 不起作用,因为在这种情况下,我不希望子类需要实现 fail,我只需要知道它是否已实现。选项 2 或 3 似乎最有可能 - 老实说,我不知道 __init_subclass__。我会检查一下,但最后一个解决方案似乎最优雅,最终最符合用例。谢谢!
      • @sadmicrowave 太棒了!很高兴我能帮上忙。
      猜你喜欢
      • 1970-01-01
      • 2016-03-29
      • 1970-01-01
      • 2011-06-01
      • 2011-05-22
      • 2013-01-02
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      相关资源
      最近更新 更多