例如,在Java中,@Override注释不仅提供了重写的编译时检查,而且可以生成优秀的自文档代码。
我只是在寻找文档(尽管如果它是一些检查器(如pylint)的指示器,那是额外的奖励)。我可以在某处添加注释或文档字符串,但在Python中指示重写的惯用方法是什么?
例如,在Java中,@Override注释不仅提供了重写的编译时检查,而且可以生成优秀的自文档代码。
我只是在寻找文档(尽管如果它是一些检查器(如pylint)的指示器,那是额外的奖励)。我可以在某处添加注释或文档字符串,但在Python中指示重写的惯用方法是什么?
当前回答
您可以使用来自PEP 544的协议。使用这种方法,接口实现关系只在使用站点声明。
假设您已经有了一个实现(让我们称之为MyFoobar),您定义了一个接口(一个协议),它具有实现的所有方法和字段的签名,我们称之为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.
现在,这还不包括在父类和实现类中都有实现的情况,比如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)
其他回答
Python不是Java。当然,没有真正的编译时检查。
我认为文档字符串中的注释就足够了。这允许您的方法的任何用户输入help(obj.method),并看到该方法是一个覆盖。
你也可以用类Foo(interface)显式地扩展一个接口,这将允许用户输入help(interface .method)来了解你的方法想要提供的功能。
如果你只是为了文档的目的,你可以定义你自己的覆盖装饰器:
def override(f):
return f
class MyClass (BaseClass):
@override
def method(self):
pass
这实际上只是花瓶,除非你创建override(f)的方式实际上是检查重写。
但是,这是Python,为什么要写成Java呢?
这里是一个没有注释的不同解决方案。
它有一个稍微不同的目标。其他建议的解决方案检查给定的方法是否实际覆盖了父方法,而这个解决方案检查是否所有的父方法都被覆盖了。
你不必引发AssertionError,但可以打印警告或在生产环境中通过检查__init__中的env并在检查之前返回它。
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}')
我创建的装饰器不仅检查覆盖属性的名称是否为该属性所在类的任何超类(无需指定超类),还检查确保覆盖属性必须与被覆盖属性的类型相同。类方法被视为方法,静态方法被视为函数。这个装饰器适用于可调用对象、类方法、静态方法和属性。
源代码见:https://github.com/fireuser909/override
此装饰器仅适用于作为重写实例的类。OverridesMeta,但是如果你的类是一个自定义元类的实例,使用create_custom_overrides_meta函数来创建一个与覆盖装饰器兼容的元类。对于测试,运行覆盖。__init__模块。
即兴对@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 answer的伟大之处在于检查发生在初始化阶段。检查不需要“运行”。参考前面的例子,类B从未初始化(B()),但NotImplementedError仍然会引发。这意味着可以更快地捕获覆盖错误。