Я способствовал намекам типа для упорядоченного набора библиотеки. Проблема в том, что, несмотря на то, что у меня есть следующие строки в файле ordered_set.pyi
:
from typing import MutableSet, TypeVar, Sequence
T = TypeVar('T')
class OrderedSet(MutableSet[T], Sequence[T]):
...
Я не могу писать:
IntOrderedSet = OrderedSet[int]
в моем коде Python поднимает:
TypeError: 'ABCMeta' object is not subscriptable
Это происходит потому, что в ordered_set.py
OrderedSet
определяется как:
from collections.abc import MutableSet, Sequence
class OrderedSet(MutableSet, Sequence):
...
Я сделал PR с фиксацией, которая меняет класс OrderedSet
для наследования от ввода классов, но владелец ordered-set
отказался принять его, потому что, как он сказал:
Импорт ввода внутри кода добавляет зависимость времени установки и времени выполнения кода. ordered_set получил как единый модуль без зависимостей в течение 6 лет. Кто-то может решить, что они не хотят иметь ничего общего с setuptools и просто отбрасывают order_set на PYTHONPATH, и это все равно будет работать. Мне бы очень хотелось потерять эти типы.
Есть ли способ поддерживать OrderedSet[int]
без изменения библиотеки ordered_set.py
?
Единственным обходным решением, о котором я знаю, является определение любых пользовательских псевдонимов типа, которые они существуют только во время проверки типа и никогда во время выполнения.
Для этого вам необходимо:
if typing.TYPE_CHECKING:
блокирует. Блок typing.TYPE_CHECKING
всегда является ложным во время выполнения и рассматривается как истинный с помощью typing.TYPE_CHECKING
типов.from __future__ import annotations
, что делает это автоматически. То есть, избегайте когда-либо оценивать подсказки типов в первую очередь.Так, например, в вашей конкретной настройке вы могли бы разместить заглушки для библиотеки OrderedSet, а затем написать свой код, чтобы выглядеть так (предположим Python 3. 7+):
from __future__ import annotations
from typing import TYPE_CHECKING
from ordered_set import OrderedSet
# Basically equivalent to 'if False'
if TYPE_CHECKING:
IntOrderedSet = OrderedSet[Int]
def expects_int_ordered_set(x: IntOrderedSet) -> None:
# blah
some_ordered_set: IntOrderedSet = OrderedSet()
Или, если вы используете Python 3.6 или ниже:
from typing import TYPE_CHECKING
from ordered_set import OrderedSet
if TYPE_CHECKING:
IntOrderedSet = OrderedSet[Int]
def expects_int_ordered_set(x: 'IntOrderedSet') -> None:
# blah
some_ordered_set: 'IntOrderedSet' = OrderedSet()
Если вы в порядке с IntOrderedSet
, мы можем обойтись без материала строки и определить IntOrderedSet
чтобы быть немного разными в момент проверки времени и времени выполнения. Например:
from typing import TYPE_CHECKING
from ordered_set import OrderedSet
if TYPE_CHECKING:
IntOrderedSet = OrderedSet[Int]
else:
IntOrderedSet = OrderedSet
def expects_int_ordered_set(x: IntOrderedSet) -> None:
# blah
some_ordered_set = IntOrderedSet()
Обязательно будьте осторожны, когда вы это делаете, - проверка типа не проверит что-либо в блоке "else"/не будет проверять, чтобы убедиться, что все, что вы там делаете, согласуется с тем, что в if TYPE_CHECKING
блок.
Конечным решением было бы просто не определять тип IntOrderedSet
в первую очередь. Это позволяет нам пропустить взломать 1, а только использовать хак 2 (это не так много взлома - это будет поведение по умолчанию в Python через несколько лет).
Так, например, мы могли бы сделать это:
from __future__ import annotations
from ordered_set import OrderedSet
def expects_int_ordered_set(x: OrderedSet[int]) -> None:
# blah
some_ordered_set: OrderedSet[int] = OrderedSet()
В зависимости от контекста, может быть, мне даже не нужно добавлять аннотацию к этому объявлению последней переменной. Здесь мы это делаем, но если бы мы определяли эту переменную внутри функции, то возможные контрольные параметры типа mypy автоматически определяли бы правильный тип для нас, основываясь на том, как мы в конечном итоге используем эту переменную.