Python 如何在子类中键入注释重写的方法?
假设我已经有了一个带有类型注释的方法:Python 如何在子类中键入注释重写的方法?,python,python-3.x,types,Python,Python 3.x,Types,假设我已经有了一个带有类型注释的方法: class Shape: def area(self) -> float: raise NotImplementedError 然后我将多次将其子类化: class Circle: def area(self) -> float: return math.pi * self.radius ** 2 class Rectangle: def area(self) -> float:
class Shape:
def area(self) -> float:
raise NotImplementedError
然后我将多次将其子类化:
class Circle:
def area(self) -> float:
return math.pi * self.radius ** 2
class Rectangle:
def area(self) -> float:
return self.height * self.width
如您所见,我大量复制了
->float
。假设我有10个不同的形状,有多种类似的方法,其中一些也包含参数。是否有一种方法可以从父类“复制”注释,类似于functools.wrapps()
对docstrings所做的操作?这可能会起作用,但我肯定会忽略边缘情况,例如其他参数:
from functools import partial, update_wrapper
def annotate_from(f):
return partial(update_wrapper,
wrapped=f,
assigned=('__annotations__',),
updated=())
它将从f.\uuuuu annotations\uuuuuu
中指定“包装器”函数的\uuuuuuu annotations\uuuuuuuuu
属性(请记住,它不是副本)
根据文档,函数的默认赋值已经包括\uuuuuuu注释\uuuuuu
,但是我可以理解为什么您不希望从包装中指定所有其他属性
然后,您可以将圆
和矩形
定义为
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}
作为一个副作用,您的方法将具有一个属性\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,在本例中,该属性将指向Shape.ar
使用类装饰器可以实现一种不太标准(如果您可以调用上面使用的update_wrapper standard)的方式来完成重写方法的处理:
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
同样,这不会处理带有附加参数的重写方法。您可以使用类装饰器来更新子类方法注释。在decorator中,您需要遍历类定义,然后只更新超类中存在的那些方法。当然,要访问超类,您需要使用it,它只是类、子类的元组,直到对象
。这里我们感兴趣的是元组中的第二个元素,它位于索引1
,因此\uuumro\uuuuu[1]
或使用。最后也是同样重要的一点是,您的装饰程序必须返回类
def wraps_annotations(cls):
mro = cls.mro()[1]
vars_mro = vars(mro)
for name, value in vars(cls).items():
if callable(value) and name in vars_mro:
value.__annotations__.update(vars(mro).get(name).__annotations__)
return cls
演示:
>>类形状:
... def区域(自)->浮动:
... 引发未实现的错误
...
>>>输入数学
>>>
>>>@wrapps\u注释
... 类圆(形状):
... def区域(自身):
... 返回math.pi*self.radius**2
...
>>>c=圆()
>>>c.区域注释__
{'return':}
>>>@wrapps\u注释
... 类矩形(形状):
... def区域(自身):
... 返回self.height*self.width
...
>>>r=矩形()
>>>右面积__
{'return':}
太棒了,非常感谢!:)我甚至没有想到装饰师。
>>> class Shape:
... def area(self) -> float:
... raise NotImplementedError
...
>>> import math
>>>
>>> @wraps_annotations
... class Circle(Shape):
... def area(self):
... return math.pi * self.radius ** 2
...
>>> c = Circle()
>>> c.area.__annotations__
{'return': <class 'float'>}
>>> @wraps_annotations
... class Rectangle(Shape):
... def area(self):
... return self.height * self.width
...
>>> r = Rectangle()
>>> r.area.__annotations__
{'return': <class 'float'>}