在 Python 中,如何指示我正在重写某个方法?

发布于 2024-07-29 01:07:15 字数 174 浏览 6 评论 0原文

例如,在 Java 中,@Override 注释不仅提供了重写的编译时检查,而且还可以生成出色的自文档代码。

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

In Java, for example, the @Override annotation not only provides compile-time checking of an override but makes for excellent self-documenting code.

I'm just looking for documentation (although if it's an indicator to some checker like pylint, that's a bonus). I can add a comment or docstring somewhere, but what is the idiomatic way to indicate an override in Python?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

再浓的妆也掩不了殇 2024-08-05 01:07:15

基于此和fwc:s答案,我创建了一个pip可安装包https://github.com/mkorpela/覆盖

有时我会在这里查看这个问题。
主要是在(再次)在我们的代码库中看到相同的错误之后发生这种情况:有人在重命名“接口”中的方法时忘记了一些“接口”实现类。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!!!!!!!

Based on this and fwc:s answer I created a pip installable package https://github.com/mkorpela/overrides

From time to time I end up here looking at this question.
Mainly this happens after (again) seeing the same bug in our code base: Someone has forgotten some "interface" implementing class while renaming a method in the "interface"..

Well Python ain't Java but Python has power -- and explicit is better than implicit -- and there are real concrete cases in the real world where this thing would have helped me.

So here is a sketch of overrides decorator. This will check that the class given as a parameter has the same method (or something) name as the method being decorated.

If you can think of a better solution please post it here!

def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider

It works as follows:

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


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'

and if you do a faulty version it will raise an assertion error during class loading:

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

>> AssertionError!!!!!!!
迷你仙 2024-08-05 01:07:15

从 python 3.12(发布日期为 2023 年秋季)开始,这是可以完成的。 我建议你看看这个网站 https://peps.python.org/pep-0698/< /a>. 它很好地解释了如何像在 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

As of python 3.12 (release date fall 2023) this can be done. I would suggest you to look at this website https://peps.python.org/pep-0698/. It explains really well how to decorate methods in Python like in Java.

Here a code example, for more details you can check out the website above.

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
独孤求败 2024-08-05 01:07:15

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

Here's an implementation that doesn't require specification of the interface_class name.

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
空城仅有旧梦在 2024-08-05 01:07:15

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

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

这实际上只是养眼,除非您以实际检查覆盖的方式创建 override(f) 。

但是,这是Python,为什么要像Java 一样写它呢?

If you want this for documentation purposes only, you can define your own override decorator:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

This is really nothing but eye-candy, unless you create override(f) in such a way that is actually checks for an override.

But then, this is Python, why write it like it was Java?

梦中楼上月下 2024-08-05 01:07:15

版本

在 @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错误。

Improvising on @mkorpela great answer, here is a version with

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

Here is what it looks like in practice:

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

results in more descriptive NotImplementedError error

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

full stack

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

results in more descriptive NotImplementedError error

function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>

full stack

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'>


The great thing about @mkorpela answer is the check happens during some initialization phase. The check does not need to be "run". Referring to the prior examples, class B is never initialized (B()) yet the NotImplementedError will still raise. This means overrides errors are caught sooner.

被翻牌 2024-08-05 01:07:15

Python 不是 Java。 当然,实际上并不存在编译时检查之类的东西。

我认为文档字符串中的注释已经足够了。 这允许您的方法的任何用户键入 help(obj.method) 并查看该方法是重写。

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

Python ain't Java. There's of course no such thing really as compile-time checking.

I think a comment in the docstring is plenty. This allows any user of your method to type help(obj.method) and see that the method is an override.

You can also explicitly extend an interface with class Foo(Interface), which will allow users to type help(Interface.method) to get an idea about the functionality your method is intended to provide.

夜灵血窟げ 2024-08-05 01:07:15

就像其他人所说的那样,与 Java 不同,没有 @Overide 标签,但是上面您可以使用装饰器创建自己的标签,但是我建议使用 getattrib() 全局方法而不是使用内部字典,这样您会得到如下所示的内容

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

:您可以在自己的 try catch 中捕获 getattr() 引发自己的错误,但我认为在这种情况下 getattr 方法更好。

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

Like others have said unlike Java there is not @Overide tag however above you can create your own using decorators however I would suggest using the getattrib() global method instead of using the internal dict so you get something like the following:

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

If you wanted to you could catch getattr() in your own try catch raise your own error but I think getattr method is better in this case.

Also this catches all items bound to a class including class methods and vairables

七堇年 2024-08-05 01:07:15

基于 @mkorpela 的精彩答案,我编写了一个类似的包(ipromise pypi github) 进行更多检查:

假设 A 继承自 BCB 继承自C

模块ipromise检查:

  • 如果Af覆盖Bf,则Bf必须存在,并且A 必须继承自B。 (这是来自覆盖包的检查)。

  • 您没有模式 Af 声明它覆盖 Bf,然后 Bf 又声明它覆盖 CfA 应该说它从 Cf 重写,因为 B 可能决定停止重写此方法,并且这不应该导致下游更新。

  • 您没有模式 Af 声明它覆盖 Cf,但 Bf 没有声明其覆盖。

  • 您没有模式 Af 声明它覆盖 Cf,但 Bf 声明它覆盖某些 Df.

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

Based on @mkorpela's great answer, I've written a similar package (ipromise pypi github) that does many more checks:

Suppose A inherits from B and C, B inherits from C.

Module ipromise checks that:

  • If A.f overrides B.f, B.f must exist, and A must inherit from B. (This is the check from the overrides package).

  • You don't have the pattern A.f declares that it overrides B.f, which then declares that it overrides C.f. A should say that it overrides from C.f since B might decide to stop overriding this method, and that should not result in downstream updates.

  • You don't have the pattern A.f declares that it overrides C.f, but B.f does not declare its override.

  • You don't have the pattern A.f declares that it overrides C.f, but B.f declares that it overrides from some D.f.

It also has various features for marking and checking implementing an abstract method.

爱人如己 2024-08-05 01:07:15

您可以使用 PEP 544 中的协议。 使用此方法,仅在使用站点声明接口-实现关系。

假设您已经有了一个实现(我们称之为 MyFoobar),您定义了一个接口(协议),它具有实现的所有方法和字段的签名,我们称之为 IFoobar 。

然后,在使用站点,您将实现实例绑定声明为具有接口类型,例如myFoobar: IFoobar = MyFoobar()。 现在,如果您使用界面中缺少的字段/方法,Mypy 将在使用站点上抱怨(即使它在运行时可以工作!)。 如果你在实现中未能实现接口中的方法,Mypy 也会抱怨。 如果你实现了接口中不存在的东西,Mypy 不会抱怨。 但这种情况很少见,因为接口定义很紧凑并且易于审查。 您将无法实际使用该代码,因为 Mypy 会抱怨。

现在,这不包括在超类和实现类中都有实现的情况,例如 ABC。 但是,即使接口中没有实现,Java 中也会使用override。 该解决方案涵盖了这种情况。

from typing import Protocol

class A(Protocol):
    def b(self):
        ...
    def d(self):  # we forgot to implement this in C
        ...

class C:
    def b(self):
        return 0

bob: A = C()

类型检查结果为:

test.py:13: error: Incompatible types in assignment (expression has type "C", variable has type "A")
test.py:13: note: 'C' is missing following 'A' protocol member:
test.py:13: note:     d
Found 1 error in 1 file (checked 1 source file)

You can use protocols from PEP 544. With this method, the interface-implementation relation is declared only at the use site.

Assuming you already have an implementation (let's call it MyFoobar), you define an interface (a Protocol), which has the signatures of all the methods and fields of your implementation, let's call that IFoobar.

Then, at the use site, you declare the implementation instance binding to have the interface type e.g. myFoobar: IFoobar = MyFoobar(). Now, if you use a field/method that is missing in the interface, Mypy will complain at the use site (even if it would work at runtime!). If you failed to implement a method from the interface in the implementation, Mypy will also complain. Mypy won't complain if you implement something that doesn't exist in the interface. But that case is rare, since the interface definition is compact and easy to review. You wouldn't be able to actually use that code, since Mypy would complain.

Now, this won't cover cases where you have implementations both in the superclass and the implementing class, like some uses of ABC. But override is used in Java even with no implementation in the interface. This solution covers that case.

from typing import Protocol

class A(Protocol):
    def b(self):
        ...
    def d(self):  # we forgot to implement this in C
        ...

class C:
    def b(self):
        return 0

bob: A = C()

Type checking results in:

test.py:13: error: Incompatible types in assignment (expression has type "C", variable has type "A")
test.py:13: note: 'C' is missing following 'A' protocol member:
test.py:13: note:     d
Found 1 error in 1 file (checked 1 source file)
冷情 2024-08-05 01:07:15

在python 3.6及以上版本中,@override提供的功能可以使用python的描述符协议轻松实现,即set_name dunder方法:

class override:
    def __init__(self, func):
       self._func = func
       update_wrapper(self, func)

    def __get__(self, obj, obj_type):
        if obj is None:
            return self
        return self._func

    def __set_name__(self, obj_type, name):
        self.validate_override(obj_type, name)

    def validate_override(self, obj_type, name):
        for parent in obj_type.__bases__:
            func = parent.__dict__.get(name, None)
            if callable(func):
                return
        else:
            raise NotImplementedError(f"{obj_type.__name__} does not override {name}")

注意,这里set_name被调用一次包装类定义完毕,我们可以通过调用其dunder方法基类来获取包装类的父类。

对于每个它的父类,我们希望通过检查函数

  1. 名称是否在类字典中来检查包装的函数是否在类中
  2. 实现,它是可调用的

使用 i 会很简单:

class AbstractShoppingCartService:
    def add_item(self, request: AddItemRequest) -> Cart:
        ...


class ShoppingCartService(AbstractShoppingCartService):
    @override
    def add_item(self, request: AddItemRequest) -> Cart:
        ...

as python 3.6 and above, the functionality provided by @override can be easily implemented using the descriptor protocol of python, namingly the set_name dunder method:

class override:
    def __init__(self, func):
       self._func = func
       update_wrapper(self, func)

    def __get__(self, obj, obj_type):
        if obj is None:
            return self
        return self._func

    def __set_name__(self, obj_type, name):
        self.validate_override(obj_type, name)

    def validate_override(self, obj_type, name):
        for parent in obj_type.__bases__:
            func = parent.__dict__.get(name, None)
            if callable(func):
                return
        else:
            raise NotImplementedError(f"{obj_type.__name__} does not override {name}")

Note that here set_name is called once the wrapped class is defined, and we can get the parent class of the wrapped class by calling its dunder method bases.

for each for its parent class, we would like to check if the wrapped function is implemented in the class by

  1. check that the function name is in the class dict
  2. it is a callable

Using i would be as simple as:

class AbstractShoppingCartService:
    def add_item(self, request: AddItemRequest) -> Cart:
        ...


class ShoppingCartService(AbstractShoppingCartService):
    @override
    def add_item(self, request: AddItemRequest) -> Cart:
        ...
桃扇骨 2024-08-05 01:07:15

我所做的装饰器不仅检查其中重写属性的名称是否是该属性所在类的任何超类,而无需指定超类,该装饰器还检查以确保重写属性必须与被重写属性具有相同的类型属性。 类方法被视为方法,静态方法被视为函数。 该装饰器适用于可调用对象、类方法、静态方法和属性。

源代码请参见: https://github.com/fireuser909/override

此装饰器仅适用于类它们是 override.OverridesMeta 的实例,但如果您的类是自定义元类的实例,请使用 create_custom_overrides_meta 函数创建与 override 装饰器兼容的元类。 对于测试,运行 override.__init__ 模块。

Not only did the decorator I made check if the name of the overriding attribute in is any superclass of the class the attribute is in without having to specify a superclass, this decorator also check to ensure the overriding attribute must be the same type as the overridden attribute. Class Methods are treated like methods and Static Methods are treated like functions. This decorator works for callables, class methods, static methods, and properties.

For source code see: https://github.com/fireuser909/override

This decorator only works for classes that are instances of override.OverridesMeta but if your class is an instance of a custom metaclass use the create_custom_overrides_meta function to create a metaclass that is compatible with the override decorator. For tests, run the override.__init__ module.

梦忆晨望 2024-08-05 01:07:15

在Python 2.6+和Python 3.2+中你可以做到这一点(实际模拟一下,Python不支持函数重载,子类会自动覆盖父类的方法)。 我们可以为此使用装饰器。 但首先请注意,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没有直接函数重载,所以即使B类不继承自A类,但需要所有这些foo您还需要使用@Override(尽管在这种情况下使用别名“Overload”看起来会更好)

In Python 2.6+ and Python 3.2+ you can do it (Actually simulate it, Python doesn't support function overloading and child class automatically overrides parent's method). We can use Decorators for this. But first, note that Python's @decorators and Java's @Annotations are totally different things. The prior one is a wrapper with concrete code while later one is a flag to compiler.

For this, first do 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)

output:

foo in A
foo in B
foo in B; arg = 4
foo in B; arg = ('Wheee', 3.14)

Note that you must have to use decorator here with parenthesis

One thing to remember is that since Python doesn't have function overloading directly, so even if Class B don't inherit from Class A but needs all those foos than also you need to use @Override (though using alias 'Overload' will look better in that case)

猫烠⑼条掵仅有一顆心 2024-08-05 01:07:15

这是一个没有注释的不同解决方案。

它有一个稍微不同的目标。 虽然其他建议的解决方案检查给定方法是否实际上覆盖了父方法,但此解决方案检查是否所有父方法都被覆盖。

您不必引发 AssertionError,但可以通过检查 __init__ 中的环境来打印警告或在生产中禁用它,并在检查之前返回。

class Parent:

    def a():
        pass

    def b():
        pass

class Child(Overrides, Parent):

    def a()

    # raises an error, as b() is not overridden


class Overrides:

    def __init__(self):
        # collect all defined methods of all base-classes
        bases = [b for b in self.__class__.__bases__ if b != Overrides]
        required_methods = set()
        for base in bases:
            required_methods = required_methods.union(set([f for f in dir(base) if not f.startswith('_')]))
        
        # check for each method in each base class (in required_methods)
        # if the class, that inherits `Overrides` implements them all
        missing = []
        # me is the fully qualified name of the CLASS, which inherits 
        # `Overrides`
        me = self.__class__.__qualname__
        for required_method in required_methods:

            # The method can be either defined in the parent or the child 
            # class. To check it, we get a reference to the method via 
            # getattr
            try:
                found = getattr(self, required_method)
            except AttributeError:
                # this should not happen, as getattr returns the method in 
                # the parent class if it is not defined in the cild class.
                # It has to be in a parent class, as the required_methods 
                # is a union of all base-class methods.
                missing.append(required_method)
                continue
            
            # here is, where the magic happens.
            # found is a reference to a method, and found.__qualname__ is
            # the full-name of the METHOD. Remember, that me is the full
            # name of the class. 
            # We want to check, where the method is defined. If it is 
            # defined in an parent class, we did no override it, thus it 
            # is missing. 
            # If we did not override, the __qualname__ is Parent.method
            # If we did override it, the __qualname__ is Child.method
            # With this fact, we can determine if the class, which uses
            # `Override` did implement it.
            if not found.__qualname__.startswith(me + '.'):
                missing.append(required_method)

        # Maybe a warning would be enough here
        if missing != []:
            raise AssertionError(f'{me} did not override these methods: {missing}')

Here is a different solution without annotation.

It has a slightly other goal in mind. While the other proposed solutions check if the given method actually overrides a parent, this one checks, if all parent methods were overridden.

You don't have to raise an AssertionError, but can print a warning or disable it in production by checking for the env in __init__ and return before checking.

class Parent:

    def a():
        pass

    def b():
        pass

class Child(Overrides, Parent):

    def a()

    # raises an error, as b() is not overridden


class Overrides:

    def __init__(self):
        # collect all defined methods of all base-classes
        bases = [b for b in self.__class__.__bases__ if b != Overrides]
        required_methods = set()
        for base in bases:
            required_methods = required_methods.union(set([f for f in dir(base) if not f.startswith('_')]))
        
        # check for each method in each base class (in required_methods)
        # if the class, that inherits `Overrides` implements them all
        missing = []
        # me is the fully qualified name of the CLASS, which inherits 
        # `Overrides`
        me = self.__class__.__qualname__
        for required_method in required_methods:

            # The method can be either defined in the parent or the child 
            # class. To check it, we get a reference to the method via 
            # getattr
            try:
                found = getattr(self, required_method)
            except AttributeError:
                # this should not happen, as getattr returns the method in 
                # the parent class if it is not defined in the cild class.
                # It has to be in a parent class, as the required_methods 
                # is a union of all base-class methods.
                missing.append(required_method)
                continue
            
            # here is, where the magic happens.
            # found is a reference to a method, and found.__qualname__ is
            # the full-name of the METHOD. Remember, that me is the full
            # name of the class. 
            # We want to check, where the method is defined. If it is 
            # defined in an parent class, we did no override it, thus it 
            # is missing. 
            # If we did not override, the __qualname__ is Parent.method
            # If we did override it, the __qualname__ is Child.method
            # With this fact, we can determine if the class, which uses
            # `Override` did implement it.
            if not found.__qualname__.startswith(me + '.'):
                missing.append(required_method)

        # Maybe a warning would be enough here
        if missing != []:
            raise AssertionError(f'{me} did not override these methods: {missing}')
遥远的她 2024-08-05 01:07:15

Hear 是最简单的,并且在 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()

Hear is simplest and working under Jython with Java classes:

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文