确定子类是不是具有在 Python 中实现的基类方法

Posted

技术标签:

【中文标题】确定子类是不是具有在 Python 中实现的基类方法【英文标题】: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' ) 的东西。我错过了什么?

【问题讨论】:

【参考方案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 太棒了!很高兴我能帮上忙。

以上是关于确定子类是不是具有在 Python 中实现的基类方法的主要内容,如果未能解决你的问题,请参考以下文章

编译器找不到在基类中实现的虚函数

在子类中实现纯虚方法

具有虚拟析构函数的基类子类中的默认析构函数

用子类填充的基类向量不起作用

python所有的内置类型汇总

多态(day10)