问题:在Python中,如何指示我要覆盖方法?
例如,在Java中,@Override注释不仅提供覆盖的编译时检查,而且还提供了出色的自记录代码。
我只是在寻找文档(尽管如果它是指示诸如pylint之类的检查器的指标,那是一个额外的好处)。我可以在某处添加注释或文档字符串,但是在Python中指示替代的惯用方式是什么?
回答 0
基于此和fwc:s的答案,我创建了一个pip可安装软件包https://github.com/mkorpela/overrides
我有时会不时地在这里看这个问题。主要是在(再次)在我们的代码库中看到相同的错误之后发生的:有人在重命名“接口”中的方法时忘记了一些“接口”实现类。
好吧,Python不是Java,但是Python具有强大的功能-显式的要比隐式的好-并且在现实世界中确实有具体的案例可以帮助我。
因此,这是替代装饰器的草图。这将检查作为参数给出的类是否具有与要修饰的方法相同的方法(或某些名称)。
如果您能想到更好的解决方案,请在此处发布!
def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider
其工作方式如下:
class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'
class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'
如果版本错误,则会在类加载期间引发断言错误:
class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'
>> AssertionError!!!!!!!
回答 1
这是一个不需要指定interface_class名称的实现。
import inspect
import re
def overrides(method):
    # actually can't do this because a method is really just a function while inside a class def'n  
    #assert(inspect.ismethod(method))
    stack = inspect.stack()
    base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1)
    # handle multiple inheritance
    base_classes = [s.strip() for s in base_classes.split(',')]
    if not base_classes:
        raise ValueError('overrides decorator: unable to determine base class') 
    # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
    derived_class_locals = stack[2][0].f_locals
    # replace each class name in base_classes with the actual class type
    for i, base_class in enumerate(base_classes):
        if '.' not in base_class:
            base_classes[i] = derived_class_locals[base_class]
        else:
            components = base_class.split('.')
            # obj is either a module or a class
            obj = derived_class_locals[components[0]]
            for c in components[1:]:
                assert(inspect.ismodule(obj) or inspect.isclass(obj))
                obj = getattr(obj, c)
            base_classes[i] = obj
    assert( any( hasattr(cls, method.__name__) for cls in base_classes ) )
    return method
回答 2
如果仅出于文档目的而希望这样做,则可以定义自己的替代装饰器:
def override(f):
    return f
class MyClass (BaseClass):
    @override
    def method(self):
        pass
除非您以一种实际上检查替代的方式创建override(f),否则这实际上只是让人眼花can乱。
但是,这就是Python,为什么要像Java一样编写它?
回答 3
Python不是Java。当然,没有真正的编译时检查之类的东西。
我认为文档字符串中的注释很多。这允许您的方法的任何用户键入help(obj.method)并看到该方法是替代。  
您还可以使用显式扩展一个接口class Foo(Interface),该接口允许用户键入help(Interface.method)以了解有关您的方法旨在提供的功能的想法。
回答 4
即兴在@mkorpela 很好的答案,这是一个版本
更精确的检查,命名和引发的Error对象
def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer https://stackoverflow.com/a/8313042/471376
    """
    def confirm_override(method):
        if method.__name__ not in dir(interface_class):
            raise NotImplementedError('function "%s" is an @override but that'
                                      ' function is not implemented in base'
                                      ' class %s'
                                      % (method.__name__,
                                         interface_class)
                                      )
        def func():
            pass
        attr = getattr(interface_class, method.__name__)
        if type(attr) is not type(func):
            raise NotImplementedError('function "%s" is an @override'
                                      ' but that is implemented as type %s'
                                      ' in base class %s, expected implemented'
                                      ' type %s'
                                      % (method.__name__,
                                         type(attr),
                                         interface_class,
                                         type(func))
                                      )
        return method
    return confirm_override
实际上是这样的:
NotImplementedError“ 未在基类中实现 ”
class A(object):
    # ERROR: `a` is not a implemented!
    pass
class B(A):
    @overrides(A)
    def a(self):
        pass
导致更多描述性NotImplementedError错误
function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
全栈
Traceback (most recent call last):
  …
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 110, in confirm_override
    interface_class)
NotImplementedError: function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
NotImplementedError“ 预期的实现类型 ”
class A(object):
    # ERROR: `a` is not a function!
    a = ''
class B(A):
    @overrides(A)
    def a(self):
        pass
导致更多描述性NotImplementedError错误
function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>
全栈
Traceback (most recent call last):
  …
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 125, in confirm_override
    type(func))
NotImplementedError: function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>
关于@mkorpela答案,很棒的事情是检查发生在某些初始化阶段。该检查不需要“运行”。参考前面的示例,class B它从未被初始化(B()),但NotImplementedError仍然会上升。这意味着overrides可以更快地发现错误。
回答 5
就像其他人所说的,与Java不同,这里没有@Overide标记,但是您可以使用装饰器创建自己的标记,但是我建议使用getattrib()全局方法,而不要使用内部dict,这样您将获得以下内容:
def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method
如果您愿意,可以在自己的尝试中捕获getattr(),这会引发您自己的错误,但我认为在这种情况下,getattr方法更好。
同样,这会捕获绑定到类的所有项目,包括类方法和可变项
回答 6
基于@mkorpela的出色回答,我编写了一个类似的软件包(ipromise pypi github),该软件包可以进行更多检查:
假设A从继承B和C,B从继承C。
ipromise模块检查:
如果
A.f覆盖B.f,则B.f必须存在,并且A必须从继承B。(这是覆盖程序包中的检查)。您没有模式
A.f声明它被覆盖B.f,然后模式声明它被覆盖C.f。A应该说它重写自,C.f因为它B可能决定停止重写此方法,并且不应导致下游更新。您没有模式
A.f声明其覆盖C.f,但B.f没有声明其覆盖。您没有模式
A.f声明它被覆盖C.f,但是B.f声明它被某些模式覆盖D.f。
它还具有用于标记和检查实现抽象方法的各种功能。
回答 7
听觉是最简单的,并且可以在Jython下使用Java类进行工作:
class MyClass(SomeJavaClass):
     def __init__(self):
         setattr(self, "name_of_method_to_override", __method_override__)
     def __method_override__(self, some_args):
         some_thing_to_do()
回答 8
我制作的装饰器不仅检查了覆盖属性的名称是否是该属性所在类的任何超类,而无需指定超类,而且该装饰器还检查以确保覆盖属性必须与被覆盖的类型相同属性。类方法被视为方法,静态方法被视为函数。此装饰器适用于可调用对象,类方法,静态方法和属性。
有关源代码,请参见:https : //github.com/fireuser909/override
此装饰器仅适用于重写类实例的类。OverridesMeta,但如果您的类是自定义元类的实例,请使用create_custom_overrides_meta函数创建与重写装饰器兼容的元类。对于测试,请运行override .__ init__模块。
回答 9
在Python 2.6+和Python 3.2+中,您可以做到(实际上是模拟它,Python不支持函数重载,并且子类会自动覆盖parent的方法)。我们可以为此使用装饰器。但是首先,请注意,Python @decorators和Java @Annotations是完全不同的东西。前一个是带有具体代码的包装器,而后一个是编译器的标志。
为此,首先 pip install multipledispatch
from multipledispatch import dispatch as Override
# using alias 'Override' just to give you some feel :)
class A:
    def foo(self):
        print('foo in A')
    # More methods here
class B(A):
    @Override()
    def foo(self):
        print('foo in B')
    
    @Override(int)
    def foo(self,a):
        print('foo in B; arg =',a)
        
    @Override(str,float)
    def foo(self,a,b):
        print('foo in B; arg =',(a,b))
        
a=A()
b=B()
a.foo()
b.foo()
b.foo(4)
b.foo('Wheee',3.14)
输出:
foo in A
foo in B
foo in B; arg = 4
foo in B; arg = ('Wheee', 3.14)
请注意,您必须在此处使用带括号的装饰器
要记住的一件事是,由于Python没有直接的函数重载,因此即使Class B不继承自Class A但需要所有这些foos,也需要使用@Override(尽管使用别名’Overload’看起来在那种情况下更好)
