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

klh5stk1  于 2024-01-05  发布在  Python
关注(0)|答案(3)|浏览(358)

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

  1. from __future__ import annotations
  2. class M:
  3. def set_width(self, width: int)->M:
  4. self.width = width
  5. return self
  6. def set_height(self, height: int)->M:
  7. self.height = height
  8. return self

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

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


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

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


现在我不能这样做:

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


我在mypy中得到以下错误:

  1. _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开始,你可以这样做:

  1. from typing import Self
  2. class M:
  3. def set_width(self, width: int) -> Self:
  4. self.width = width
  5. return self

字符串

bihw5rsg

bihw5rsg2#

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

  1. from __future__ import annotations
  2. import asyncio
  3. from typing import TypeVar
  4. T = TypeVar('T')
  5. class M:
  6. def set_width(self: T, width: int)->T:
  7. self.width = width
  8. return self
  9. def set_height(self: T, height: int)->T:
  10. self.height = height
  11. return self
  12. def copy(self)->M:
  13. return M().set_width(self.width).set_height(self.height)
  14. class M3D(M):
  15. def set_depth(self: T, depth: int) -> T:
  16. self.depth = depth
  17. return self
  18. box = M().set_width(5).set_height(10) # box has correct type
  19. cube = M3D().set_width(2).set_height(3).set_depth(5) # cube has correct type
  20. 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原则)
因此,我将假设注解挂起注解控件是有原因的,并使用它们(我称之为 * 不要在这里打扰我 *)。

展开查看全部

相关问题