How to ensure (with a warning) that parent functions call child elements that override them?

I have a class Parentwith many children:

class Parent(Object):
    def function(self):
        do_something()

class Child1(Parent):
    def function(self):
        super(Child1, self).function()
        do_something_else_1()

class Child2(Parent):
    def function(self):
        do_something_else_2()

etc.

It almost always happens that it do_something()should be called by children. I would like to throw a warning if someone writes a child class without calling super, for example, in Child2. How can I do it?

+4
source share
2 answers

The first solution that is not hidden from the end user:

, , - , . , func_code.co_names (func.__code__.co_names Py3), , super func.func_name (func.__name__ Py3):

def require_super(func):
    f_name = func.func_name
    other_names = func.func_code.co_names
    if not all(val in other_names for val in [f_name, 'super']):
        warnings.warn("Super must be defined on function: " + f_name)
    return func

f_name - , , .

other_names , , arent ( , ..).), -.

all(val in other_names for val in [f_name, 'super']) other_names f_name super . , , , , .

, warning :

class foo(Parent):
    @require_super
    def function(self, arg):
        s = "No super"

:

UserWarning: Super must be defined on function: function

/, , .

, , , . require_super, . , () code, , , .


:

- . , , type(cls) = mymetaclass , . require_super staticmethod ( , ) , , .

:

class requireMeta(type):

    _requiredFunc = 'function'

    def __new__(cls, *args):
        # base classes, dictionary of functions
        bases, funcs = args[1], args[2]
        # skip the check for the base class
        # which inherits from object
        if not bases[0].__name__ == 'object':  
            try:
                # run requireSuper for the function 'function'
                cls.requireSuper(funcs[cls._requiredFunc])
            except KeyError:
                # if it doesn't exist, complain.
                warnings.warn("Function: {} must be defined!".format(cls._requiredFunc))            
        return super(requireMeta, cls).__new__(cls, *args)

    @staticmethod
    def requireSuper(func):
        """
        Functionality is the same as described already.
        """
        f_name = func.func_name
        other_names = func.func_code.co_names
        if not all(val in other_names for val in [f_name, 'super']):
            warnings.warn("Super must be defined on function: " + f_name)
        return func

, , __new__: , , , object ( Py3) , 'function' require_super. , ; , require_super.

Parent requireMeta, __metaclass__:

class Parent(object):
    __metaclass__ = requireMeta

    def function(self):
        print("Doing Something")

Py3 class Parent(metaclass = requireMeta) :-).

Python , requireMeta.__new__ . , Parent Parent, Child1 Child2 , , __new__

class Child1(Parent):
   def function(self):
       super(Child1, self).function()
       do_something_else_1()

. , :

class Child2(Parent):
    def function(self):
        do_something_else_2()

:

 UserWarning: Super must be defined on function: function

function :

class Child3(Parent):
    pass

:

UserWarning: Function: function must be overriden!
+3

- , "" , super:

class Parent(object):
    def _extra_functions(self):
        # Override me in derived classes!
        pass

    def function(self):
        # Don't override me
        do_something()
        self.extra_functions()

class Child1(Parent):
    def _extra_functions(self):
        do_something_else_1()

class Child2(Parent):
    def _extra_functions(self):
        do_something_else_2()

, , . , , "" , , , python, , . , ( ) , .

+4

Source: https://habr.com/ru/post/1626933/


All Articles