如何从Python中的基类保护函数?

时间:2019-04-15 18:14:33

标签: python python-3.x override subclassing

我目前是python-dotenv PyPI page Python中的模板方法模式。

我想知道是否有什么方法可以保护某些函数免受基类的影响,以使子类无法覆盖?如下所示,子类中的_primitive_operation_3覆盖了基类中的相同函数。

import abc

class AbstractClass(metaclass=abc.ABCMeta):
    """
    Define abstract primitive operations that concrete subclasses define
    to implement steps of an algorithm.
    Implement a template method defining the skeleton of an algorithm.
    The template method calls primitive operations as well as operations
    defined in AbstractClass or those of other objects.
    """
    def template_method(self):
        self._primitive_operation_1()
        self._primitive_operation_2()
        self._primitive_operation_3()

    # Functions must be specified by subclass (i.e., subclass variant)
    @abc.abstractmethod
    def _primitive_operation_1(self):
        pass

    # Functions must be specified by subclass (i.e., subclass variant)
    @abc.abstractmethod
    def _primitive_operation_2(self):
        pass

    # Functions inherited and not modified by subclass (i.e., subclass invariant)
    def _primitive_operation_3(self):
        print ('Execute operation #3 from main class')

class ConcreteClass(AbstractClass):
    """
    Implement the primitive operations to carry out
    subclass-specificsteps of the algorithm.
    """
    def _primitive_operation_1(self):
        pass

    def _primitive_operation_2(self):
        pass

    # You can still overwrite it if you want
    def _primitive_operation_3(self):
        print ('Execute operation #3 from subclass')

def main():
    concrete_class = ConcreteClass()
    concrete_class.template_method()

if __name__ == "__main__":
    main()

如果无法防止基类中的方法被覆盖,那么我该如何放置一些内容以发出自动警报/警告,指示基类中的特定方法已被覆盖?

1 个答案:

答案 0 :(得分:1)

您不能阻止子类使用相同的名称,不可以。您可以通过为名称加上双下划线前缀来保护名称以防意外阴影:

def __primitive_operation_3(self):
    print('Execute operation #3 from main class')

Python编译器将在类的方法内替换对该名称的 all 引用,以添加该类名作为前缀。在这里,它是AbstractClass,因此实际名称为_AbstractClass__primitive_operation_3,但是由于编译器会重写所有引用,因此您透明地继续在代码中使用__primitive_operation_3

子类上的任何__primitive_operation_3名称都将使用不同的前缀重命名,这仅仅是因为它们是在具有不同名称的类上定义的。

此功能明确地针对希望允许子类在其定义中使用各种名称的基类。

请参阅词法分析参考文档中的Reserved classes of identifiers section

  

__*

     

类私有名称。在类定义的上下文中使用时,此类别中的名称将被重写,以使用变形的形式来帮助避免基类和派生类的“私有”属性之间的名称冲突。

和表达式文档的Identifiers section

  

私人名称处理:当在类定义中以文本形式出现的标识符以两个或多个下划线字符开头且不以两个或多个下划线结尾时,则被视为该类的私有名称。在为专用名称生成代码之前,专用名称会转换为更长的格式。转换将在类名前面插入类名,并删除前导下划线,并插入单个下划线。例如,出现在名为__spam的类中的标识符Ham将转换为_Ham__spam。此转换独立于使用标识符的句法上下文。如果转换后的名称过长(超过255个字符),则可能会发生实现定义的截断。如果类名仅包含下划线,则不进行任何转换。

子类仍然可以覆盖名称,但必须显式包含相同的前缀。

请注意,您不能使用这种机制来避免在子类中覆盖特殊方法(带有____init__前导和尾随双下划线)。如果基类的子类在不注意调用基实现的情况下不能覆盖特定方法,则清除项目文档至关重要。充其量您可以通过检查是否缺少副作用来检测子类是否在覆盖方法(这是标准库具有protected Thread.__init__ overriding的方式,或者您可以在调用该方法之前检查__len__是否为真。< / p>