Python如何输入anotate一个返回self的方法?

klh5stk1  于 4个月前  发布在  Python
关注(0)|答案(3)|浏览(56)

假设我有一个实现方法链的类:

from __future__ import annotations

class M:
    def set_width(self, width: int)->M:
        self.width = width
        return self

    def set_height(self, height: int)->M:
        self.height = height
        return self

字符串
我可以这样使用它:

box = M().set_width(5).set_height(10)


这是可行的,但如果我有一个子类M3D:

class M3D(M):
    def set_depth(self, depth: int) -> M3D:
        self.depth = depth
        return self


现在我不能这样做:

cube = M3D().set_width(2).set_height(3).set_depth(5)


我在mypy中得到以下错误:

_test_typeanotations.py:21: error: "M" has no attribute "set_depth"; maybe "set_width"


因为set_width()返回一个没有方法set_depthM。我已经看到建议为每个子类重写set_width()set_height()以指定正确的类型,但这将是为每个方法编写的大量代码。必须有一个更简单的方法。
这也与特殊方法有关,例如__enter__传统上返回self,所以如果有一种方法来指定它,而不需要在子类中提到它,那就太好了。

kb5ga3dv

kb5ga3dv1#

从Python 3.11开始,你可以这样做:

from typing import Self

class M:
    def set_width(self, width: int) -> Self:
        self.width = width
        return self

字符串

bihw5rsg

bihw5rsg2#

经过大量的研究和分析,我找到了一种在mypy中有效的方法,尽管Pycham有时仍然会猜错类型。
技巧是让self成为var类型:

from __future__ import annotations

import asyncio
from typing import TypeVar

T = TypeVar('T')

class M:
    def set_width(self: T, width: int)->T:
        self.width = width
        return self

    def set_height(self: T, height: int)->T:
        self.height = height
        return self

    def copy(self)->M:
        return M().set_width(self.width).set_height(self.height)

class M3D(M):
    def set_depth(self: T, depth: int) -> T:
        self.depth = depth
        return self

box = M().set_width(5).set_height(10) # box has correct type
cube = M3D().set_width(2).set_height(3).set_depth(5) # cube has correct type
attemptToTreatBoxAsCube = M3D().copy().set_depth(4) # Mypy gets angry as expected

字符串
最后一行在mypy中运行良好,但pycharm有时仍然会自动完成set_depth,即使在M3D上调用时,.copy()实际上返回M

wribegjk

wribegjk3#

这在任何使用继承的语言中都是一个经典问题。不同的语言对此有不同的处理方式:

  • 在C++中,您将在调用set_depth之前强制转换set_height的结果
  • 在Java中,您可以使用与C++相同的强制转换,或者让IDE生成一堆重写,只手动更改重写方法中的类型。

Python是一种动态类型语言,所以没有强制转换指令。所以你有三种可能的方法:

  • 勇敢的方法:覆盖所有相关的方法来调用基方法,并在返回注解中声明新类型
  • I don't care 方式:annotation控件只给出警告。因为你知道这行代码是好的,你可以忽略警告
  • Don't Bother Me 方式:注解在Python中是可选的,注解控制通常可以通过特殊注解挂起。这里知道没有问题,所以你可以安全地挂起该指令或该方法的类型控制。

以下仅是我的意见。
如果可能的话,我会避免使用“不要打扰”的方式,因为如果你在代码中留下警告,那么如果有新的警告,你将不得不在每次更改后进行控制。
我不会仅仅为了摆脱警告而重写方法。毕竟Python是一种动态类型语言,甚至允许鸭子类型。如果我知道代码是正确的,我会避免添加无用的代码(DRY和KISS原则)
因此,我将假设注解挂起注解控件是有原因的,并使用它们(我称之为 * 不要在这里打扰我 *)。

相关问题