在 Python 中,如何指示我正在重写某个方法?
例如,在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
基于此和fwc:s答案,我创建了一个pip可安装包https://github.com/mkorpela/覆盖
有时我会在这里查看这个问题。
主要是在(再次)在我们的代码库中看到相同的错误之后发生这种情况:有人在重命名“接口”中的方法时忘记了一些“接口”实现类。Python
不是 Java,但 Python 拥有强大的功能——而且是显式的比隐含的要好——现实世界中有一些真实的具体案例,这件事会对我有所帮助。
这是重写装饰器的草图。 这将检查作为参数给出的类是否与被修饰的方法具有相同的方法(或其他名称)名称。
如果您能想到更好的解决方案,请在这里发布!
它的工作原理如下:
如果您执行了错误的版本,它将在类加载期间引发断言错误:
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!
It works as follows:
and if you do a faulty version it will raise an assertion error during class loading:
从 python 3.12(发布日期为 2023 年秋季)开始,这是可以完成的。 我建议你看看这个网站 https://peps.python.org/pep-0698/< /a>. 它很好地解释了如何像在 Java 中一样在 Python 中修饰方法。
这是一个代码示例,有关更多详细信息,您可以查看上面的网站。
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.
这是一个不需要指定 interface_class 名称的实现。
Here's an implementation that doesn't require specification of the interface_class name.
如果您只想将此用于文档目的,您可以定义自己的覆盖装饰器:
这实际上只是养眼,除非您以实际检查覆盖的方式创建 override(f) 。
但是,这是Python,为什么要像Java 一样写它呢?
If you want this for documentation purposes only, you can define your own override decorator:
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?
版本
在 @mkorpela 很好的答案上进行即兴创作,这是一个具有更精确检查、命名和引发的 Error 对象的
这里实际情况如下:
NotImplementedError
“未在基类中实现”会导致更具描述性的
NotImplementedError
错误完整堆栈
NotImplementedError
“预期实现的类型”导致更具描述性的
NotImplementedError
错误完整堆栈
@mkorpela 答案的伟大之处在于检查发生在某些初始化阶段。 该检查不需要“运行”。 参考前面的示例,
class B
从未初始化 (B()
),但NotImplementedError
仍会引发。 这意味着可以更快地捕获overrides
错误。Improvising on @mkorpela great answer, here is a version with
more precise checks, naming, and raised Error objects
Here is what it looks like in practice:
NotImplementedError
"not implemented in base class"results in more descriptive
NotImplementedError
errorfull stack
NotImplementedError
"expected implemented type"results in more descriptive
NotImplementedError
errorfull stack
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 theNotImplementedError
will still raise. This meansoverrides
errors are caught sooner.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 typehelp(Interface.method)
to get an idea about the functionality your method is intended to provide.就像其他人所说的那样,与 Java 不同,没有 @Overide 标签,但是上面您可以使用装饰器创建自己的标签,但是我建议使用 getattrib() 全局方法而不是使用内部字典,这样您会得到如下所示的内容
:您可以在自己的 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:
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
基于 @mkorpela 的精彩答案,我编写了一个类似的包(ipromise pypi github) 进行更多检查:
假设
A
继承自B
和C
,B
继承自C
。模块ipromise检查:
如果
Af
覆盖Bf
,则Bf
必须存在,并且A
必须继承自B
。 (这是来自覆盖包的检查)。您没有模式
Af
声明它覆盖Bf
,然后Bf
又声明它覆盖Cf
。A
应该说它从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 fromB
andC
,B
inherits fromC
.Module ipromise checks that:
If
A.f
overridesB.f
,B.f
must exist, andA
must inherit fromB
. (This is the check from the overrides package).You don't have the pattern
A.f
declares that it overridesB.f
, which then declares that it overridesC.f
.A
should say that it overrides fromC.f
sinceB
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 overridesC.f
, butB.f
does not declare its override.You don't have the pattern
A.f
declares that it overridesC.f
, butB.f
declares that it overrides from someD.f
.It also has various features for marking and checking implementing an abstract method.
您可以使用 PEP 544 中的协议。 使用此方法,仅在使用站点声明接口-实现关系。
假设您已经有了一个实现(我们称之为 MyFoobar),您定义了一个接口(协议),它具有实现的所有方法和字段的签名,我们称之为 IFoobar 。
然后,在使用站点,您将实现实例绑定声明为具有接口类型,例如
myFoobar: IFoobar = MyFoobar()
。 现在,如果您使用界面中缺少的字段/方法,Mypy 将在使用站点上抱怨(即使它在运行时可以工作!)。 如果你在实现中未能实现接口中的方法,Mypy 也会抱怨。 如果你实现了接口中不存在的东西,Mypy 不会抱怨。 但这种情况很少见,因为接口定义很紧凑并且易于审查。 您将无法实际使用该代码,因为 Mypy 会抱怨。现在,这不包括在超类和实现类中都有实现的情况,例如
ABC
。 但是,即使接口中没有实现,Java 中也会使用override
。 该解决方案涵盖了这种情况。类型检查结果为:
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 thatIFoobar
.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
. Butoverride
is used in Java even with no implementation in the interface. This solution covers that case.Type checking results in:
在python 3.6及以上版本中,@override提供的功能可以使用python的描述符协议轻松实现,即set_name dunder方法:
注意,这里set_name被调用一次包装类定义完毕,我们可以通过调用其dunder方法基类来获取包装类的父类。
对于每个它的父类,我们希望通过检查函数
使用 i 会很简单:
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:
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
Using i would be as simple as:
我所做的装饰器不仅检查其中重写属性的名称是否是该属性所在类的任何超类,而无需指定超类,该装饰器还检查以确保重写属性必须与被重写属性具有相同的类型属性。 类方法被视为方法,静态方法被视为函数。 该装饰器适用于可调用对象、类方法、静态方法和属性。
源代码请参见: 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.
在Python 2.6+和Python 3.2+中你可以做到这一点(实际模拟一下,Python不支持函数重载,子类会自动覆盖父类的方法)。 我们可以为此使用装饰器。 但首先请注意,Python 的
@decorators
和 Java 的@Annotations
是完全不同的东西。 前一个是带有具体代码的包装器,而后一个是编译器的标志。为此,首先执行
pip install multipledispatch
输出:
要记住的一件事是,由于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
output:
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
foo
s than also you need to use @Override (though using alias 'Overload' will look better in that case)这是一个没有注释的不同解决方案。
它有一个稍微不同的目标。 虽然其他建议的解决方案检查给定方法是否实际上覆盖了父方法,但此解决方案检查是否所有父方法都被覆盖。
您不必引发
AssertionError
,但可以通过检查__init__
中的环境来打印警告或在生产中禁用它,并在检查之前返回。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.Hear 是最简单的,并且在 Jython 下使用 Java 类工作:
Hear is simplest and working under Jython with Java classes: