Тип подсказка, где один аргумент является типом другого - PullRequest
2 голосов
/ 28 апреля 2020

Мне трудно выразить точные подсказки типа для пары параметров, где один должен быть значением, являющимся экземпляром некоторого типа, а другой должен быть самим типом или каким-то супертипом.

Один случай, когда вы видите это в Python, - это метод __exit__ для менеджера контекста (класса).

import typing as t
from types import TracebackType
from contextlib import AbstractContextManager

class Acme(AbstractContextManager):
  def __exit__(self, exc_type: t.Type[Exception], exc: Exception,
      tb: Tracebacktype) -> None:
    ...

Этот конкретный случай c может не иметь значения, потому что менеджеры контекста обрабатываются внутри Python, но я все же хотел бы понять, как express два аргумента, где первый является (супер-) типом второго.

Концептуально мой Проблема в том, что я хочу express, чтобы значение exc имело тип exc_type или какой-либо подтип. В приведенном выше выражении я думаю, что mypy был бы вполне доволен аргументами вроде LookupError, RuntimeError('foo'), хотя RuntimeError не является типом LookupError. Есть ли лучший способ для express, когда mypy поймает такую ​​ошибку?

Обновление

Попытка теста здесь с использованием TypeVars:

import typing as t

C = t.TypeVar('C', bound=t.Collection)

def f(t: t.Type[C], c: C) -> None:
  pass

f(t.Dict, [])

Я ожидаю, что mypy будет жаловаться на этот код, потому что хотя пустой список является типом коллекции, это не словарь.

Ответы [ 2 ]

2 голосов
/ 28 апреля 2020

Этот вид использования Type[some_type_var], по-видимому, не рекомендуется в течение секунды в PEP , где предоставляется только подмножество этой поддержки (особенно для привязки TypeVar s к типам классов). Похоже, что это разочарование не является философией (это будет обычное использование обобщений в языке, где типы и значения являются первоклассными), но больше относится к практической реализации средств проверки типов. Использование TypeVar для сигнатур метода класса - частный случай вашего вопроса - было даже поздним дополнением к pep .


Возможные решения, которые не работают с mypy (на данный момент) :

Стандарт TypeVar

Нет проверки типов в сигнатурах функций Type[some_type_var]. Это не переменная величина, основанная на some_type_var, равном contravariant или covariant (и не должно быть).

TypeVar из Type[TypeVar]

T = TypeVar("T")
Meta = TypeVar("Meta", bound=Type[T])

def foo(the_type: Meta, the_value: T):
   ....

T и Meta не могут "связываться" вместе.


К моему удивлению, в некоторых случаях вы можете выжать немного из этого поведения (но это особый случай и мне кажется, это неопределенно)

T = TypeVar('T', str, int, float, bytes)

class Foo:

    def bar(self, the_type: Type[T], the_value: T):
        print(isinstance(the_value, the_type))

f = Foo()
f.bar(str, "baz")
f.bar(str, b"baz")  # wrong!
f.bar(int, 1)
f.bar(int, 3.15159)  # wrong! (but no complaint)
f.bar(float, 1.0)
f.bar(float, 1)  # wrong! (but no complaint)
f.bar(float, b'1.0')  # wrong!

Предоставление

so.py:nn: error: Value of type variable "T" of "bar" of "Foo" cannot be "object"
so.py:nn: error: Value of type variable "T" of "bar" of "Foo" cannot be "object"
Found 2 errors in 1 file (checked 1 source file)

Но только на первый взгляд для примитивных типов python (это не будет работать с типами пользовательского пространства) (и снова только некоторые из python примитивных типов, как показано (см. error-miss с float и int). Я думаю, что это ясно расширение "to-have" TypeVar (и делает возможным Generic вариантов использования).


В mypy есть некоторые связанные с этим проблемы:

  1. о поведении аннотаций класса
  2. TypeVar из TypeVar
1 голос
/ 28 апреля 2020

Вот для чего TypeVar с! Вы хотите что-то вроде:

from types import TracebackType
from typing import Type, TypeVar
from contextlib import AbstractContextManager

_E = TypeVar('_E', bound=Exception)

class Acme(AbstractContextManager):
    def __exit__(
        self,
        exc_type: Type[_E],
        exc: _E,
        tb: Tracebacktype
    ) -> None:
    ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...