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

Blu*_*luu 148 python inheritance overriding self-documenting-code

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

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

mko*_*ela 185

更新(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
Run Code Online (Sandbox Code Playgroud)

它的工作原理如下:

class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'
Run Code Online (Sandbox Code Playgroud)

如果你做了一个错误的版本,它会在类加载期间引发一个断言错误:

class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'

>> AssertionError!!!!!!!
Run Code Online (Sandbox Code Playgroud)

  • 真棒.这是我第一次尝试时发现的拼写错误.荣誉. (15认同)
  • mfbutner:每次执行方法时都不会调用它 - 只有在创建方法时才会调用它. (7认同)
  • @mkorpela,嘿,这个你的代码应该在python默认的lib系统中.为什么你不把这个放在pip系统?:P (5认同)
  • @mkorpela:哦,我建议通知python核心开发人员这个包,他们可能想要考虑向核心python系统添加overover decorator.:) (4认同)
  • 这对于doc字符串也很好!如果重写方法没有自己的方法,`overrides`可以复制重写方法的docstring. (3认同)
  • 需要注意的是:[Python包](https://github.com/mkorpela/overrides)比原始草图复杂得多,不再需要重新指定基类. (3认同)
  • 如果被覆盖的方法用`@abstractmethod` 标记,那么您还可以检查相同的参数列表。 (2认同)

Lor*_*lli 38

从 python 3.12(发布日期为 2023 年秋季)开始,这是可以完成的。我建议您查看这个网站https://peps.python.org/pep-0698/。它很好地解释了如何像在 Java 中一样在 Python 中修饰方法。

这是一个代码示例,有关更多详细信息,您可以查看上面的网站。

from typing import override

class Parent:
    def foo(self) -> int:
        return 1

    def bar(self, x: str) -> str:
        return x

class Child(Parent):
    @override
    def foo(self) -> int:
        return 2

    @override
    def baz() -> int:  # Type check error: no matching signature in ancestor
        return 1
Run Code Online (Sandbox Code Playgroud)


fwc*_*fwc 27

这是一个不需要指定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
Run Code Online (Sandbox Code Playgroud)

  • @ larham1这个装饰器在分析类定义时执行一次,而不是在每次调用时执行.因此,与程序运行时相比,它的执行成本是无关紧要的. (4认同)
  • 有点神奇,但使典型用法变得容易得多。您可以包括用法示例吗? (2认同)

Ber*_*Ber 13

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

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass
Run Code Online (Sandbox Code Playgroud)

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

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

  • *但是,这就是Python,为什么要像Java那样编写它?*因为Java中的一些想法很好并且值得扩展到其他语言? (58认同)
  • 因为当你在超类中重命名一个方法时,知道某个子类2级别向下覆盖它会很好.当然,它很容易检查,但语言解析器的一些帮助不会受到伤害. (9认同)
  • 因为这是个好主意.各种其他语言都具有该功能的事实无论是赞成还是反对. (4认同)
  • 可以通过检查将实际验证添加到`override`装饰器. (2认同)

Tri*_*ych 6

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

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

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

  • Java中`@Override`的真正意义不在于 - 当你打算覆盖一个方法时会抓到一个错误,但最终定义一个新的(例如因为你拼错了一个名字;在Java中,它也可能之所以发生是因为你使用了错误的签名,但这不是Python中的一个问题 - 但拼写错误仍然是). (52认同)
  • @siamii我认为对文档的帮助很棒,但在我看到的所有官方Java文档中,它们只表明了编译时间检查的重要性.请证实您对帕维尔"错误"的说法. (6认同)
  • @ Pavel Minaev:没错,但是对于文档来说仍然很方便,特别是如果你使用的IDE /文本编辑器没有覆盖的自动指示器(例如,Eclipse的JDT将它们整齐地显示在行号旁边). (2认同)
  • @PavelMinaev错了.除了编译时间检查之外,`@Override`的要点之一是文档. (2认同)

Jam*_*979 6

即兴创作@mkorpela很好的答案,这里有一个版本

更精确的检查、命名和引发的 Error 对象

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer /sf/answers/581912971/
    """
    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
Run Code Online (Sandbox Code Playgroud)


下面是它在实践中的样子:

NotImplementedError未在基类中实现

class A(object):
    # ERROR: `a` is not a implemented!
    pass

class B(A):
    @overrides(A)
    def a(self):
        pass
Run Code Online (Sandbox Code Playgroud)

导致更多描述性NotImplementedError错误

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
Run Code Online (Sandbox Code Playgroud)

全栈

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'>
Run Code Online (Sandbox Code Playgroud)


NotImplementedError预期实现类型

class A(object):
    # ERROR: `a` is not a function!
    a = ''

class B(A):
    @overrides(A)
    def a(self):
        pass
Run Code Online (Sandbox Code Playgroud)

导致更多描述性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'>
Run Code Online (Sandbox Code Playgroud)

全栈

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'>
Run Code Online (Sandbox Code Playgroud)




@mkorpela 答案的好处是检查发生在某个初始化阶段。检查不需要“运行”。参考前面的示例,class B从未初始化 ( B()) 但NotImplementedError仍然会引发。这意味着overrides可以更快地发现错误。