如何强制(带警告)父函数被覆盖它们的子函数调用?

时间:2016-02-01 23:06:03

标签: python python-2.7

我有一个有许多孩子的Parent课程:

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()

等等。

几乎所有孩子都应该调用do_something()。如果有人在没有进行super调用的情况下编写子课程,我会发出警告,例如Child2。我该怎么做?

2 个答案:

答案 0 :(得分:4)

您可以实现类似的一种方法是为子类提供一个“强制”的界面。一个子类的编写器来获取基本行为,甚至不必调用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可能不是正确的语言。但是,这个(或其他答案)非常清楚开发人员如何假设扩展此类。

答案 1 :(得分:3)

第一个没有向最终用户隐藏的解决方案:

另一种不那么全面的方法你可以通过使用一个功能并检查其内部的装饰器。这个装饰者可以做的是检查func_code.co_names(Py3中的func.__code__.co_names)以查看super以及函数名func.func_name(Py3中的func.__name__)是否为本:

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 is a tuple containing the names which aren’t covered by any of the other fields (they are not local variables, they are not free variables, etc) used by the bytecode.

all(val in other_names for val in [f_name, 'super'])将浏览other_names中的姓名并检查f_namesuper是否作为条目存在。 如果 ,它会发出警告然后返回该函数,如果是这种情况,则返回该函数。

当用户定义一个类并且没有提供超级调用时,将在执行类主体期间引发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 的任何子类进行的,因此您的子类Child1Child2可以保持原样并且在调用元类__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!