例如,在Java中,@Override注释不仅提供了重写的编译时检查,而且可以生成优秀的自文档代码。
我只是在寻找文档(尽管如果它是一些检查器(如pylint)的指示器,那是额外的奖励)。我可以在某处添加注释或文档字符串,但在Python中指示重写的惯用方法是什么?
例如,在Java中,@Override注释不仅提供了重写的编译时检查,而且可以生成优秀的自文档代码。
我只是在寻找文档(尽管如果它是一些检查器(如pylint)的指示器,那是额外的奖励)。我可以在某处添加注释或文档字符串,但在Python中指示重写的惯用方法是什么?
当前回答
Python不是Java。当然,没有真正的编译时检查。
我认为文档字符串中的注释就足够了。这允许您的方法的任何用户输入help(obj.method),并看到该方法是一个覆盖。
你也可以用类Foo(interface)显式地扩展一个接口,这将允许用户输入help(interface .method)来了解你的方法想要提供的功能。
其他回答
下面是一个不需要指定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
基于这个和fwc:s的答案,我创建了一个pip可安装包https://github.com/mkorpela/overrides
我经常在这里看到这个问题。 这主要发生在(再次)在我们的代码库中看到相同的错误之后:有人在重命名“接口”中的方法时忘记了一些“接口”实现类。
好吧,Python不是Java,但Python有强大的功能——显式比隐式好——在现实世界中,有一些真实的具体案例,这个东西会帮助我。
这是overrides decorator的草图。这将检查作为参数给出的类是否与被修饰的方法具有相同的方法(或其他东西)名称。
如果你能想到一个更好的解决方案,请张贴在这里!
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!!!!!!!
您可以使用来自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)
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()
我创建的装饰器不仅检查覆盖属性的名称是否为该属性所在类的任何超类(无需指定超类),还检查确保覆盖属性必须与被覆盖属性的类型相同。类方法被视为方法,静态方法被视为函数。这个装饰器适用于可调用对象、类方法、静态方法和属性。
源代码见:https://github.com/fireuser909/override
此装饰器仅适用于作为重写实例的类。OverridesMeta,但是如果你的类是一个自定义元类的实例,使用create_custom_overrides_meta函数来创建一个与覆盖装饰器兼容的元类。对于测试,运行覆盖。__init__模块。