在Python中,如何表示我正在重写一个方法?
在Java中,比如说,@Override
这个标记不仅可以在编译时检查你是否正确地覆盖了一个方法,还能让代码更容易理解,像是给代码加了注释一样。
我现在想找一些文档(虽然如果它能被像pylint这样的检查工具识别,那就更好了)。我可以在某个地方加个注释或者文档字符串,但在Python中,表示覆盖的方法的标准做法是什么呢?
14 个回答
31
这里有一个实现方法,不需要指定接口类的名称。
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
40
从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
271
根据这个和fwc的回答,我创建了一个可以用pip安装的包,链接是https://github.com/mkorpela/overrides
我时不时会来到这里看看这个问题。主要是因为我又一次在我们的代码中发现了同样的bug:有人在重命名“接口”中的方法时,忘记了某个“接口”实现类。
虽然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!!!!!!!