强制子类在覆盖它时调用父方法 [英] Force child class to call parent method when overriding it

查看:154
本文介绍了强制子类在覆盖它时调用父方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇Python中是否有一种方法强制(从Parent类)当一个父类方法被重写时从一个子类中调用。



例如:

  class Parent(object):

def __init __(self):
self.someValue = 1

def start(self):
'''强制方法被调用'''
self.someValue + = 1

正确实现我想让我的孩子Child类做的是:

  class ChildCorrect(Parent):

def start(self):
Parent.start(self)
self .someValue + = 1

但是,有一种方法可以强制开发Child类的开发人员特别调用不要只是覆盖)Parent类中定义的'start'方法,以防他们忘记调用它:

  class ChildIncorrect ):

def start(self):
'''父方法未调用,但正确覆盖'''
self.someValue + = 1



此外,如果这不是最佳实践,那么什么是替代方案?

解决方案

如何(不)做



不,没有安全的方法强制用户调用super。让我们讨论一些选项,这将达到或类似的目标,并讨论为什么这是一个坏主意。在下一节中,我还将讨论处理这种情况的明智方法(针对Python社区)。


  1. 元类可以在定义子类时检查是否覆盖目标方法(=与目标方法具有相同的名称)使用适当的参数调用super。



    这需要深入实施特定的行为,例如使用 dis 模块。没有任何情况下,我可以想象这是一个好主意 - 它取决于CPython的具体版本,并且事实上,你是使用CPython。


  2. 元类可以与基本类进行协作。在这种情况下,当调用继承方法时,基类会通知元类,并且元类用一段保护代码封装任何覆盖方法。



    保护代码测试继承方法在覆盖方法的执行期间被调用。这具有缺点,需要用于需要该特征的每个方法的单独的通知信道,并且另外线程安全性将是关注的。此外,覆盖方法已经完成执行,在您注意到它没有调用继承的方法,这可能是坏的取决于您的方案。



    它也打开了一个问题:如果覆盖方法没有调用继承方法怎么办?提高异常可能是代码使用方法意外的(或者更糟的是,它可能假设该方法没有任何效果,这是不真实的)。




  3. 元类可以生成一段守护码对于在覆盖方法执行之前或之后自动调用继承方法的每个覆盖方法



    这里的缺点是开发人员不会期望继承的方法被自动调用并且没有办法停止该调用(例如,如果不满足子类的特殊前提条件)或者在它们自己的覆盖方法中调用继承方法时控制。



    这在编码python时违反了很多明智的原则(避免意外,显式优于隐式,可能更多)。


  4. p>点3和4的组合。使用来自点3的基 - 和元类的协作,来自点4的保护码可以被扩展以自动调用超级 iff 覆盖方法没有调用超级自身。



    同样,这是意想不到的,但它解决了重复的超级调用以及如何处理不调用超级的方法的问题。



    但是,仍有问题。虽然线程安全性可以用线程本地化来固定,但是当不满足前提条件时,除了通过提出在所有情况下可能不期望的异常之外,仍然没有用于重写方法来中止对super的调用。此外,只有在覆盖方法之后才能自动调用super,而不是之前的方法,这在某些情况下也是不希望的。


此外,这没有帮助在对象和类的生命期期间重新绑定属性,虽然这可以通过使用描述符和/或扩展元类来帮助它



为什么不这样做




最佳实践,什么是替代方案?


Python的常见最佳实践是假设你是同意的成年人。这意味着,没有人正在积极地尝试对你的代码做讨厌的事情,除非你允许他们。在这样的生态系统中,在方法或类的文档中修改 .. warning :: 是有意义的,以便从该类继承的任何人知道他们有什么



此外,在合适的点调用super方法在许多上下文中是有意义的,开发人员使用基类将无论如何都会考虑它,并且只是偶尔忘记它。在这种情况下,使用上面第三点的元类不会有帮助 - 用户必须记住来调用super,这本身就是一个问题,尤其是对于有经验的程序员。



它也违反了最少惊讶的原则,显式优于隐式(没有人希望继承的方法被隐式调用!)。这又要记录得很好,在这种情况下,你也可以诉诸只是没有超级被自动调用,只是文档,它使得比通常更有意义调用继承的方法。


I am curious whether there is a way in Python to force (from the Parent class) for a parent method to be called from a child class when it is being overridden.

Example:

class Parent(object):

    def __init__(self):
        self.someValue=1

    def start(self):
        ''' Force method to be called'''
        self.someValue+=1

The correct implementation of what I wanted my child Child class to do would be:

class ChildCorrect(Parent):

    def start(self):
        Parent.start(self)
        self.someValue+=1

However, is there a way to force developers that develop Child classes to specifically call(not just override) the 'start' method defined in the Parent class in case they forget to call it:

class ChildIncorrect(Parent):

    def start(self):
        '''Parent method not called, but correctly overridden'''
        self.someValue+=1

Also, if this is not considered best practice, what would be an alternative?

解决方案

How (not) to do it

No, there is no safe way to force users to call super. Let us go over a few options which would reach that or a similar goal and discuss why it’s a bad idea. In the next section, I will also discuss what the sensible (with respect to the Python community) way to deal with the situation is.

  1. A metaclass could check, at the time the subclass is defined, whether a method overriding the target method (= has the same name as the target method) calls super with the appropriate arguments.

    This requires deeply implementation-specific behaviour, such as using the dis module of CPython. There are no circumstances where I could imagine this to be a good idea—it is dependent on the specific version of CPython and on the fact that you are using CPython at all.

  2. A metaclass could co-operate with the baseclass. In this scenario, the baseclass notifies the metaclass when the inherited method is called and the metaclass wraps any overriding methods with a piece of guard code.

    The guard code tests whether the inherited method got called during the execution of the overriding method. This has the downside that a separate notification channel for each method which needs this "feature" is required, and in addition thread-safety would be a concern. Also, the overriding method has finished execution at the point you notice that it has not called the inherited method, which may be bad depending on your scenario.

    It also opens the question: What to do if the overriding method has not called the inherited method? Raising an exception might be unexpected by the code using the method (or, even worse, it might assume that the method had no effect at all, which is not true).

    Also the late feedback the developer overriding the class (if they get that feedback at all!) is bad.

  3. A metaclass could generate a piece of guard code for every overriden method which calls the inherited method automatically before or after the overriding method has executed.

    The downside here is developers will not be expecting that the inherited method is being called automatically and have no way to stop that call (e.g. if a precondition special to the subclass is not met) or control when in their own overriding method the inherited method is called.

    This violates a good bunch of sensible principles when coding python (avoid surprises, explicit is better than implicit, and possibly more).

  4. A combination of point 3 and 4. Using the co-operation of base- and metaclass from point 3, the guard code from point 4 could be extended to automatically call super iff the overriding method has not called super themselves.

    Again, this is unexpected, but it resolves the issues with duplicate super calls and how to handle a method which does not call super.

    However, there are still remaining problems. While thread-safety could be fixed with thread-locals, there is still no way for an overriding method to abort the call to super when a precondition is not met, except by raising an exception which may not be desirable in all cases. Also, super can only be called automatically after the overriding method, not before, which, again, in some scenarios is undesirable.

Also, none of this help against re-binding the attribute during the lifetime of the object and class, although this can be helped by using descriptors and/or extending the metaclass to take care of it.

Why not to do it

Also, if this is not considered best practice, what would be an alternative?

A common best-practice with Python is to assume that you are among consenting adults. That means, that noone is actively trying to do nasty things to your code, unless you allow them to. In such an ecosystem, it would make sense to tack a .. warning:: in the documentation of the method or class, so that anyone inheriting from that class knows what they have to do.

Also, calling the super method at an appropriate point makes sense in so many contexts that developers using the base class will consider it anyways and only forget about it accidentally. In such a case, using the metaclass from the third point above would not help either—users would have to remember not to call super, which might be an issue by itself, especially for experienced programmers.

It also violates the principle of least surprise and "explicit is better than implicit" (noone expects the inherited method to be called implicitly!). This, again, would have to be documented well, in which case you can also resort to just not have super be called automatically and just document that it makes even more sense than usual to call the inherited method.

这篇关于强制子类在覆盖它时调用父方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆