Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何在子类中键入注释重写的方法?_Python_Python 3.x_Types - Fatal编程技术网

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'>}