这可能会起作用,尽管我肯定会错过边缘情况,例如附加参数:
from functools import partial, update_wrapper
def annotate_from(f):
return partial(update_wrapper,
wrapped=f,
assigned=('__annotations__',),
updated=())
这将分配“包装”函数的__annotations__
属性来自f.__annotations__
(请记住,它不是副本)。
根据文件显示update_wrapper https://docs.python.org/3/library/functools.html#functools.update_wrapper函数的默认值是assigned包括__annotations__
已经,但我可以明白为什么您不想将所有其他属性分配给wrapped.
这样您就可以定义您的Circle
and Rectangle
as
class Circle:
@annotate_from(Shape.area)
def area(self):
return math.pi * self.radius ** 2
class Rectangle:
@annotate_from(Shape.area)
def area(self):
return self.height * self.width
和结果
In [82]: Circle.area.__annotations__
Out[82]: {'return': builtins.float}
In [86]: Rectangle.area.__annotations__
Out[86]: {'return': builtins.float}
作为副作用,你的方法将有一个属性__wrapped__
,这将指向Shape.area
在这种情况下。
一个不太标准的(如果你可以调用上面的使用更新包装器标准)方法可以使用类装饰器来实现处理重写方法:
from inspect import getmembers, isfunction, signature
def override(f):
"""
Mark method overrides.
"""
f.__override__ = True
return f
def _is_method_override(m):
return isfunction(m) and getattr(m, '__override__', False)
def annotate_overrides(cls):
"""
Copy annotations of overridden methods.
"""
bases = cls.mro()[1:]
for name, method in getmembers(cls, _is_method_override):
for base in bases:
if hasattr(base, name):
break
else:
raise RuntimeError(
'method {!r} not found in bases of {!r}'.format(
name, cls))
base_method = getattr(base, name)
method.__annotations__ = base_method.__annotations__.copy()
return cls
进而:
@annotate_overrides
class Rectangle(Shape):
@override
def area(self):
return self.height * self.width
同样,这不会处理带有附加参数的重写方法。