我目前learning在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()
如果无法阻止来自基类的方法被覆盖,我怎么能放置一些东西来提供一个自动警报/警告,指示基类中的特定方法是否已被覆盖?
您无法阻止子类使用相同的名称,不能。但是,您可以通过为名称提供双下划线前缀来保护名称免受意外阴影:
def __primitive_operation_3(self):
print('Execute operation #3 from main class')
Python编译器将在类的方法中替换对该名称的所有引用,以将类名添加为前缀。这里是AbstractClass
,所以实际名称变成_AbstractClass__primitive_operation_3
,但是因为编译器重写了所有引用,所以你在代码中透明地继续使用__primitive_operation_3
。
子类上的任何__primitive_operation_3
名称都将使用不同的前缀重命名,因为它们是在具有不同名称的类上定义的。
此功能明确针对希望允许子类在其定义中使用各种名称的基类。
请参阅词法分析参考文档中的Reserved classes of identifiers section:
__*
类私有名称。当在类定义的上下文中使用时,此类别中的名称将被重写以使用损坏的表单来帮助避免基类和派生类的“私有”属性之间的名称冲突。
和表达式文档的Identifiers section:
专用名称修改:当在类定义中以文本方式出现的标识符以两个或多个下划线字符开头且不以两个或多个下划线结尾时,它将被视为该类的私有名称。在为其生成代码之前,将私有名称转换为更长的形式。转换将在名称前面插入类名,删除前导下划线并插入单个下划线。例如,在名为
__spam
的类中出现的标识符Ham
将转换为_Ham__spam
。此转换独立于使用标识符的语法上下文。如果转换后的名称非常长(超过255个字符),则可能会发生实现定义的截断。如果类名仅由下划线组成,则不进行转换。
子类仍然可以覆盖名称,但必须显式包含相同的前缀。
请注意,您不能使用此机制来避免在子类中重写特殊方法(使用前导和尾随__
双下划线,例如__init__
或__len__
)。如果基类的子类无法覆盖特定方法而无需调用基本实现,那么清晰的项目文档是最重要的。通过检查缺少的副作用(标准库具有protected Thread.__init__
overriding的方式,或者在调用方法之前仍然可以检查self.methodname.__func__ is ClassObject.methodname
是否为真),您最多可以检测子类是否覆盖方法。