当值是 cls 的实例时,您可以注释 return 类型吗?
Can you annotate return type when value is instance of cls?
给定一个 class 和一个用于初始化的辅助方法:
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
方法的return类型?
我已经尝试了 cls
和 TrivialClass
但 PyCharm 将它们标记为未解决的引用,这在当时听起来是合理的。
使用通用类型表示您将return创建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(...)
任何子class覆盖class方法,然后returning 父class的实例(TrivialClass
或仍然是祖先的子 class)将被检测为错误,因为工厂方法被定义为 returning 类型 cls
的实例。
bound
参数指定 T
必须是 TrivialClass
的(subclass);因为在定义泛型时 class 尚不存在,所以您需要使用 forward reference (带有名称的字符串)。
参见 PEP 484 的 Annotating instance and class methods section。
注:本回答第一次修改提倡使用前向引用
将 class 本身命名为 return 值,但是 issue 1212 使得使用泛型成为可能,这是一个更好的解决方案。
从 Python 3.7 开始,当您使用 from __future__ import annotations
启动模块时,您 可以 避免在注释中使用前向引用,但创建一个 TypeVar()
模块级别的对象不是注解。即使在 Python 3.10 中也是如此,它推迟了注释中的所有类型提示解析。
注解return类型的一种简单方法是使用字符串作为class方法的return值的注解:
# 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
从 Python 3.7 开始你可以使用 __future__.annotations
:
from __future__ import annotations
class TrivialClass:
# ...
@classmethod
def from_int(cls, int_arg: int) -> TrivialClass:
# ...
return cls(...)
编辑:您不能在不覆盖类方法的情况下对 TrivialClass
进行子类化,但如果您不需要它,那么我认为它比前向引用更整洁。
在 Python 3.11 中,使用新的 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)
这也适用于子 类。
这在 PEP 673 中有描述。 Use in Classmethod Signatures 部分中使用的示例完全等同于此问题。
我打算在 Python 3.11 发布后(2022-10-03 截至目前)接受这个答案。
给定一个 class 和一个用于初始化的辅助方法:
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
方法的return类型?
我已经尝试了 cls
和 TrivialClass
但 PyCharm 将它们标记为未解决的引用,这在当时听起来是合理的。
使用通用类型表示您将return创建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(...)
任何子class覆盖class方法,然后returning 父class的实例(TrivialClass
或仍然是祖先的子 class)将被检测为错误,因为工厂方法被定义为 returning 类型 cls
的实例。
bound
参数指定 T
必须是 TrivialClass
的(subclass);因为在定义泛型时 class 尚不存在,所以您需要使用 forward reference (带有名称的字符串)。
参见 PEP 484 的 Annotating instance and class methods section。
注:本回答第一次修改提倡使用前向引用 将 class 本身命名为 return 值,但是 issue 1212 使得使用泛型成为可能,这是一个更好的解决方案。
从 Python 3.7 开始,当您使用 from __future__ import annotations
启动模块时,您 可以 避免在注释中使用前向引用,但创建一个 TypeVar()
模块级别的对象不是注解。即使在 Python 3.10 中也是如此,它推迟了注释中的所有类型提示解析。
注解return类型的一种简单方法是使用字符串作为class方法的return值的注解:
# 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
从 Python 3.7 开始你可以使用 __future__.annotations
:
from __future__ import annotations
class TrivialClass:
# ...
@classmethod
def from_int(cls, int_arg: int) -> TrivialClass:
# ...
return cls(...)
编辑:您不能在不覆盖类方法的情况下对 TrivialClass
进行子类化,但如果您不需要它,那么我认为它比前向引用更整洁。
在 Python 3.11 中,使用新的 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)
这也适用于子 类。
这在 PEP 673 中有描述。 Use in Classmethod Signatures 部分中使用的示例完全等同于此问题。
我打算在 Python 3.11 发布后(2022-10-03 截至目前)接受这个答案。