get_type_hints на TypeVar - PullRequest
       20

get_type_hints на TypeVar

0 голосов
/ 11 марта 2020

С учетом такой сигнатуры функции:

T = typing.TypeVar('T')

def foo(arg: T) -> '?':
  return arg.op()

Тип возвращаемого значения функции совпадает с типом возвращаемого значения метода "op" типа arg. Мне кажется, что эта информация должна выводиться с помощью IDE.

class SomeClass:
  def op() -> int:
    ...

x = foo(SomeClass()) # IDE should know that x has type 'int'.

Существует ли синтаксис подсказки типа, как я могу описать эту зависимость из сигнатуры функции foo? Другими словами, каким выражением я должен заменить '?' в приведенном выше коде, чтобы получить желаемый эффект?

1 Ответ

1 голос
/ 12 марта 2020

Использование TypeVar не будет работать здесь. Так как ваш TypeVar не ограничен (не объявляет себя ограниченным одним или несколькими типами), вы можете заменить его любым произвольным типом - object, int, str, float, Dict [str, List [str]] ...

Это означает, что с вашей текущей сигнатурой типа ввода тип возвращаемого значения невыразим: с учетом входных данных иногда просто просто нет действительного типа возвращаемого значения.

В более широком смысле, независимо от того, используется ли ваш инструмент способен выводить тип возвращаемого значения на самом деле не имеет значения здесь. Система типов PEP 484 не может позволить вам объявить возвращаемый тип, который основан исключительно на чем-то внутри тела функции. Вместо этого вы должны либо объявить, что возвращаемый тип является чем-то фиксированным и конкретным (например, int или str), либо типом generi c, который каким-либо образом связан с вводом.

Есть два различных способа сделать последнее.

Во-первых, вы можете настаивать на том, что любой класс с методом op() наследуется от некоторого класса, и этот класс должен выводить op быть родовым c. Например:

from typing import Generic, TypeVar

TOp = TypeVar('TOp')

class Operation(Generic[TOp]):
    def op(self) -> TOp: pass

class SomeClass(Operation[int]):
    def op(self) -> int:
        # ...snip...
        return 4

class OtherClass(Operation[str]):
    def op(self) -> str:
        # ...snip...
        return "output"

# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')

def foo(arg: Operation[T]) -> T:
    return arg.op()

# Deduces the output types should be int and str respectively
a = foo(SomeClass())
b = foo(OtherClass())

Наложение этих ограничений на arg, в свою очередь, позволяет нам определить тип возвращаемого значения.

Единственным недостатком этого подхода является то, что изменение SomeClass и OtherClass может возможно быть немного навязчивым. Если вы предпочитаете не делать этого, вместо этого вы можете создать собственный Протокол :

# Or, if you need to use older versions of Python, pip-install typing-extensions
# and do 'from typing_extensions import Protocol'.
from typing import Generic, Protocol, TypeVar

TOp = TypeVar('TOp')

# Doing 'class Blah(Protocol[T])' is a shorthand for
# doing 'class Blah(Protocol, Generic[T])'.
class SupportsOp(Protocol[TOp]):
    def op(self) -> TOp: ...

# SomeClass and OtherClass do *not* inherit SupportsOp!

class SomeClass:
    def op(self) -> int:
        # ...snip...
        return 4

class OtherClass:
    def op(self) -> str:
        # ...snip...
        return "output"

# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')

def foo(arg: SupportsOp[T]) -> T:
    return arg.op()

# Still deduces the output types should be int and str respectively

a = foo(SomeClass())
b = foo(OtherClass())

Основное отличие состоит в том, что протоколы используют структурный подтип . номинальный подтип . С номинальным подтипом тип A считается подтипом B, если A явно наследуется от B. Со структурным подтипом A является подтипом B, если его методы соответствуют сигнатурам методов, приведенным в B.


Конечно, будет ли ваша IDE способна выполнять вывод типов для дженериков, подобных этим, это отдельный вопрос. У средств проверки типов, таких как mypy, определенно не возникнет проблем, а IDE, такие как PyCharm, обычно большую часть времени выполняют достаточно хорошо, но вам, возможно, придется провести некоторое тестирование, чтобы увидеть, что поддерживается и что не поддерживается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...