[英]Python how to type anotate a method that returns self?
Suppose I have a class that implements method chaining:假设我有一个实现方法链接的 class:
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
I could use it like this:我可以这样使用它:
box = M().set_width(5).set_height(10)
This works, but if I have a subclass M3D:这可行,但如果我有一个子类 M3D:
class M3D(M):
def set_depth(self, depth: int) -> M3D:
self.depth = depth
return self
Now I can't do this:现在我不能这样做:
cube = M3D().set_width(2).set_height(3).set_depth(5)
I get the following error in mypy:我在 mypy 中收到以下错误:
_test_typeanotations.py:21: error: "M" has no attribute "set_depth"; maybe "set_width"
Because set_width()
returns an M
which has no method set_depth
.因为
set_width()
返回一个没有方法set_depth
的M
。 I have seen suggestions to override set_width()
and set_height()
for every subclass to specify the correct types, but that would be a lot of code to write for each method.我已经看到建议为每个子类覆盖
set_width()
和set_height()
以指定正确的类型,但这将是为每个方法编写的大量代码。 There has to be a easier way.必须有一个更简单的方法。
This is also relevant for special methods, for example __enter__
traditionally returns self
, so it would be nice to have a way to specify this without needing to even mention it in subclasses.这也与特殊方法有关,例如
__enter__
传统上返回self
,因此最好有一种方法来指定它而无需在子类中提及它。
This is a classic problem in any language using inheritance.这是使用 inheritance 的任何语言中的经典问题。 And it is handled differently by the languages:
语言的处理方式不同:
set_height
before calling set_depth
set_height
之前set_depth
的结果 Python is a dynamically typed language, so there is no cast instruction. Python 是动态类型语言,所以没有强制转换指令。 So you are left with 3 possible ways:
所以你有3种可能的方式:
The following is only my opinion.以下只是我的看法。
I would avoid the don't bother way if possible, because if you will leave warnings in your code, you would have to later control after each and every change if there is a new warning.如果可能的话,我会避免不打扰的方式,因为如果您将在代码中留下警告,那么如果有新的警告,您将不得不在每次更改后进行控制。
I would not override methods just to get rid of a warning.我不会仅仅为了摆脱警告而重写方法。 After all Python is a dynamically typed language that even allows duck typing.
毕竟 Python 是一种动态类型语言,甚至允许鸭子类型。 If I know that the code is correct I would avoid adding useless code (DRY and KISS principles)
如果我知道代码是正确的,我会避免添加无用的代码(DRY 和 KISS 原则)
SO I will just assume that comments to suspend annotation controls were invented for a reason and use them (what I call don't bother me here ).因此,我将假设暂停注释控件的注释是出于某种原因而发明的并使用它们(我所说的在这里不会打扰我)。
In Python 3.11 and its later versions, you will be able to do this:在 Python 3.11 及其更高版本中,您将能够执行此操作:
from typing import Self
class M:
def set_width(self, width: int) -> Self:
self.width = width
return self
After a lot of research and expirimentation, I have found a way that works in mypy, though Pycham still guesses the type wrong sometimes.经过大量的研究和实验,我找到了一种在 mypy 中有效的方法,尽管 Pycham 有时仍然会猜错类型。
The trick is to make self
a type var:诀窍是使
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
The last line specifically works fine in mypy but pycharm will still autocomplete set_depth
sometimes even though .copy()
actually returns an M
even when called on a M3D
.最后一行在 mypy 中特别有效,但
set_depth
有时仍会自动完成 set_depth ,即使.copy()
实际上返回M
,即使在M3D
上调用也是如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.