38

给定一个带有用于初始化的辅助方法的类:

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方法的返回类型?

我都试过了clsTrivialClass但是 PyCharm 将它们标记为未解析的引用,这在当时听起来很合理。

4

4 回答 4

75

使用泛型类型表示您将返回以下实例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(subclass of) TrivialClass; 因为在定义泛型时该类还不存在,所以您需要使用前向引用(带有名称的字符串)。

请参阅 PEP 484 的注释实例和类方法部分


注意:此答案的第一个修订版提倡使用将类本身命名为返回值的前向引用,但问题 1212使使用泛型成为可能,这是一个更好的解决方案。

从 Python 3.7 开始,您可以避免在使用 启动模块时在注释中使用前向引用from __future__ import annotations,但在模块级别创建TypeVar()对象不是注释。即使在 Python 3.10 中也是如此,它推迟了注解中的所有类型提示解析。

于 2016-08-29T11:49:14.813 回答
8

从 Python 3.7 开始,您可以使用__future__.annotations

from __future__ import annotations


class TrivialClass:
    # ...

    @classmethod
    def from_int(cls, int_arg: int) -> TrivialClass:
        # ...
        return cls(...)

编辑:你不能在TrivialClass不覆盖类方法的情况下进行子类化,但如果你不需要这个,那么我认为它比前向引用更整洁。

于 2019-11-05T10:21:05.430 回答
5

注解返回类型的一种简单方法是使用字符串作为类方法返回值的注解:

# 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
于 2018-03-12T16:45:29.540 回答
0

在 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 月 3 日),我计划接受这个答案。

于 2022-02-09T11:11:45.973 回答