当值是cls的实例时,你能注释返回类型吗?

Jon*_*tan 19 python type-hinting pycharm

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

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)
Run Code Online (Sandbox Code Playgroud)

是否可以注释方法的返回类型from_int

我试过两个cls,TrivialClass但是PyCharm将它们标记为未解决的引用,这在那个时间点听起来很合理.

Mar*_*ers 35

使用泛型类型表示您将返回以下实例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(...)
Run Code Online (Sandbox Code Playgroud)

任何子类重写类方法但随后返回父类的实例(TrivialClass或仍然是祖先的子类)将被检测为错误,因为工厂方法被定义为返回类型的实例cls.

bound参数指定T必须是一个(子类)TrivialClass; 因为在定义泛型时类尚不存在,所以需要使用前向引用(带有名称的字符串).

请参阅PEP 484 的Annotating instance和class methods部分.


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

  • @Jonatan:请注意,`TrivialClass` 的任何子类也将满足类型提示。 (2认同)
  • 类似的解决方案可以应用于带有“self”的类方法:“def create_something(self: T) -> T” (2认同)

Jon*_*tan 9

在 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)
Run Code Online (Sandbox Code Playgroud)

这也适用于子类。

class TrivialSubClass(TrivialClasss):
    ...

TrivialSubclass.from_int(42)
Run Code Online (Sandbox Code Playgroud)

IDE 显示返回类型TrivialSubClass而不是TrivialClass.

PEP 673对此进行了描述。


rus*_*heb 7

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

from __future__ import annotations


class TrivialClass:
    # ...

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

Run Code Online (Sandbox Code Playgroud)

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

  • 与诺亚的答案相同的问题:您对类方法的返回类型进行了硬编码。 (3认同)

Noa*_*ore 6

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

# 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)
Run Code Online (Sandbox Code Playgroud)

这传递mypy 0.560并且没有来自python的错误:

$ mypy test.py --disallow-untyped-defs --disallow-untyped-calls
$ python test.py
Run Code Online (Sandbox Code Playgroud)

  • 但是,如果不重写classmethod,你现在不能继承TrivialClass.我的第一个版本使用了与此类似的前向引用. (3认同)