Подпись ввода Python для экземпляра подкласса?

#python #mypy #python-typing

#питон #мой #набор текста на python

Вопрос:

Рассмотреть:

 from __future__ import annotations

class A:
    @classmethod
    def get(cls) -> A:
        return cls()

class B(A):
    pass

def func() -> B: # Line 12
    return B.get()
 

Запустив mypy на этом, мы получаем:

 $ mypy test.py
test.py:12: error: Incompatible return value type (got "A", expected "B")
Found 1 error in 1 file (checked 1 source file)
 

Кроме того, я проверил, работают ли рекурсивные аннотации в старом стиле. То есть:

 # from __future__ import annotations

class A:
    @classmethod
    def get(cls) -> "A":
# ...
 

….. безрезультатно.

Конечно, можно было бы сделать:

 from typing import cast

def func() -> B: # Line 12
    return cast(B, B.get())
 

Каждый раз, когда всплывает это дело. Но я бы хотел избежать этого.

Как следует это напечатать?

Комментарии:

1. Я не уверен в том, о чем вы спрашиваете. Если вы хотите вернуть A , вам нужно явно указать его. Если вы просто хотите правильно аннотировать свою функцию, почему бы и нет def func() -> A ?

2. Я действительно хочу вернуться B func . Например, предположим, что у нас есть классы, представляющие сущности базы данных. У нас есть общий класс Entity и подклассы, такие как User и Project . У этих подклассов также есть дополнительные специфические атрибуты, которые имеют значение. Мы хотим четко указать, возвращаете ли вы a User или Project потому, что это ограничит то, что мы можем сделать ниже по потоку.

Ответ №1:

Параметры cls и self обычно выводятся с помощью mpyp , чтобы избежать большого количества избыточного кода, но при необходимости они могут быть указаны явно с помощью аннотаций.

В этом случае явный тип для метода класса будет выглядеть следующим образом:

 class A:
    @classmethod
    def get(cls: Type[A]) -> A:
        return cls()
 

Итак, что нам действительно нужно здесь, так это способ создания Type[A] универсального параметра, такого, чтобы при вызове метода класса из дочернего класса вы могли ссылаться на дочерний класс. К счастью, у нас есть TypeVar значения для этого.

Работая с этим в вашем существующем примере, мы получим следующее:

 from __future__ import annotations

from typing import TypeVar, Type


T = TypeVar('T')


class A:
    @classmethod
    def get(cls: Type[T]) -> T:
        return cls()


class B(A):
    pass


def func() -> B:
    return B.get()
 

Теперь mypy я снова должен быть твоим другом! 😎