给定一个带有初始化辅助方法的类:
class TrivialClass:
def __init__(self, str_arg: str):
self.string_attribute = str_arg
@classmethod
def from_int(cls, int_arg: int) -> ?:
str_arg = str(int_arg)
return cls(str_arg)
是否可以注释
from_int
方法的返回类型?
我尝试了
cls
和 TrivialClass
,但 PyCharm 将它们标记为未解析的引用,这在当时听起来很合理。
typing.Self
对象。对于较旧的 Python 版本,您可以使用 typing-extensions
项目: 获取相同的对象
try:
from typing import Self
except ImportError:
from typing_extensions import Self
class TrivialClass:
# ...
@classmethod
def from_int(cls, int_arg: int) -> Self:
# ...
return cls(...)
请注意,在这种情况下您不需要注释
cls
。
警告:mypy 对
Self
类型的支持尚未发布;您需要等待0.991之后的下一个版本。 Pyright 已经支持了。
如果您等不及 Mypy 支持,那么您可以使用 generic type 来指示您将返回
cls
的实例:
from typing import Type, TypeVar
T = TypeVar('T', bound='TrivialClass')
class TrivialClass:
# ...
@classmethod
def from_int(cls: Type[T], int_arg: int) -> T:
# ...
return cls(...)
任何重写类方法但随后返回父类(
TrivialClass
或仍然是祖先的子类)的实例的子类都将被检测为错误,因为工厂方法被定义为返回cls
的类型。
bound
参数指定T
必须是TrivialClass
(的子类);因为在定义泛型时该类还不存在,所以需要使用 forward 引用(带有名称的字符串)。
请参阅 PEP 484 的注释实例和类方法部分。
注意:此答案的第一次修订提倡使用前向引用 将类本身命名为返回值,但是issue 1212使得可以使用泛型来代替,这是一个更好的解决方案。
从 Python 3.7 开始,当您使用 from __future__ import annotations
启动模块时,您可以避免在注释中使用前向引用,但在模块级别创建
TypeVar()
对象不是注释。即使在 Python 3.10 中也是如此,它推迟了注释中的所有类型提示解析。
__future__.annotations
:
from __future__ import annotations
class TrivialClass:
# ...
@classmethod
def from_int(cls, int_arg: int) -> TrivialClass:
# ...
return cls(...)
编辑:你不能在不重写类方法的情况下子类化
TrivialClass
,但如果你不需要这个,那么我认为它比前向引用更简洁。
在 Python 3.11 中,有一种更好的方法可以使用新的 Self 类型来做到这一点:
from typing import Self
class TrivialClass:
def __init__(self, str_arg: str):
self.string_attribute = str_arg
@classmethod
def from_int(cls, int_arg: int) -> Self:
str_arg = str(int_arg)
return cls(str_arg)
这也适用于子类。
class TrivialSubClass(TrivialClasss):
...
TrivialSubclass.from_int(42)
IDE 显示返回类型
TrivialSubClass
而不是 TrivialClass
。
这在PEP 673中进行了描述。
注释返回类型的一个简单方法是使用字符串作为类方法的返回值的注释:
# test.py
class TrivialClass:
def __init__(self, str_arg: str) -> None:
self.string_attribute = str_arg
@classmethod
def from_int(cls, int_arg: int) -> 'TrivialClass':
str_arg = str(int_arg)
return cls(str_arg)
这通过了 mypy 0.560 并且 python 没有错误:
$ mypy test.py --disallow-untyped-defs --disallow-untyped-calls
$ python test.py