在Python中,我如何表明我重写了一个方法?

问题描述 投票:134回答:8

例如,在Java中,@Override注释不仅提供了覆盖的编译时检查,而且还提供了出色的自我记录代码。

我只是在寻找文件(尽管如果它是一些像pylint这样的检查器的指示器,那就是奖金)。我可以在某处添加注释或docstring,但是在Python中指示覆盖的惯用方法是什么?

python inheritance override self-documenting-code
8个回答
177
投票

更新(2015年5月23日):基于这个和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!!!!!!!

26
投票

这是一个不需要指定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

11
投票

如果您只想将其用于文档目的,则可以定义自己的覆盖装饰器:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

除非你以实际检查覆盖的方式创建覆盖(f),否则这实际上只是眼花缭乱。

但是,这就是Python,为什么要把它写成Java呢?


6
投票

Python不是Java。当然没有像编译时检查那样的东西。

我认为文档字符串中的注释很多。这允许您的方法的任何用户键入help(obj.method)并查看该方法是一个覆盖。

您还可以使用class Foo(Interface)显式扩展接口,这将允许用户键入help(Interface.method)以了解您的方法要提供的功能。


2
投票

就像其他人说的那样,不像Java那样没有@Overide标签,但是上面你可以创建自己的使用装饰器但是我建议使用getattrib()全局方法而不是使用内部dict,所以你得到类似如下的内容:

def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method

如果你想在你自己的尝试中捕获getattr(),请抓住引发自己的错误,但我认为在这种情况下getattr方法更好。

此外,它还捕获绑定到类的所有项,包括类方法和变量


1
投票

即兴创作@mkorpela great answer,这是一个版本

more precise checks, naming, and raised Error objects

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 "not implemented in base class"

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 "expected implemented type"

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错误很快就会被发现。


1
投票

根据@ 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.

它还具有各种用于标记和检查实现抽象方法的功能。


0
投票

听听最简单,在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()
© www.soinside.com 2019 - 2024. All rights reserved.