#python #type-hinting #python-typing
Вопрос:
Название может быть немного двусмысленным, так как я не уверен, как точно сформулировать то, что я пытаюсь сделать.
Я хотел бы воспроизвести что-то подобное этому шаблону из C в python:
template<typename T>
struct Foo
{
using t = T; // Alias T so it can be used in Bar
};
template<typename T>
struct Bar
{
// The type T from Foo<T> can be used as a return type here
typename T::t fn()
{
// do something that returns T::t
}
};
int main(){
// x is determined to be a float
auto x = Bar<Foo<float>>().fn();
};
С точки зрения python, я хотел бы специализировать универсальный тип Bar[T]
на другом специализированном типе Foo[T]
, а затем использовать тип, который использовался для специализации Foo
, для ввода подсказки Bar
.
Что-то вроде
from typing import TypeVar, Generic
T = TypeVar("T")
class Foo(Generic[T]):
...
class Bar(Generic[Foo[T]]): # <-- This is illegal
def fn(self) -> T:
...
# In the following, I would like the type checker to know that x is an int.
x = Bar[Foo[int]]().fn()
Я знаю, что эта связь может быть выведена при создании экземпляров Bar
, если у нас есть что-то вроде
class Bar(Generic[T]):
def __init__(self, foo: Foo[T]):
...
но это не совсем соответствует моей нынешней проблеме.
Я бы предпочел иметь возможность создавать семейство специализаций Foo[T]
, Bar[T]
, Baz[T]
и т. Д. , Не повторяясь T
много раз. В моем реальном случае использования типы больше похожи Foo[R, S, T]
, и повторение типов довольно утомительно и подвержено ошибкам, и концептуально классы Bar
и Baz
т. Д. Рассматриваются Не как зависящие от типа T
, а скорее от конкретного типа Foo[T]
.
Так что было бы неплохо иметь возможность делать что-то вроде
MyFoo = Foo[int]
MyBar = Bar[MyFoo]
MyBaz = Baz[MyFoo]
Комментарии:
1. Вы можете попробовать использовать
typing.Union
, например,Union[int, str]
2. К сожалению, я не думаю, что это возможно с текущим синтаксисом ввода Python.
Ответ №1:
Читая документацию mypy, я наткнулся на следующее: Расширенное использование самотипов
Используя TypeVar
ограничение Foo
in Bar
, мы можем указать, что Bar
оно параметризовано чем Foo
-то подобным. Затем в рамках отдельных методов мы можем аннотировать self
вложенный тип Bar[Foo[T]]
, вызывающий T
привязку к параметру параметризации Foo
параметризации Bar
.
from __future__ import annotations
from typing import TypeVar, Generic
T = TypeVar("T")
class Foo(Generic[T]):
...
# You may want to make `S` covariant with `covariant=True`
# if you plan on using subclasses of `Foo` as parameters to `Bar`.
S = TypeVar("S", bound=Foo)
class Bar(Generic[S]):
def fn(self: Bar[Foo[T]]) -> T:
...
# Below, when binding `fn`, `self` is recognized as
# being of type `Bar[Foo[int]]` which binds `T` as `int`
# and hence the return type is also `int`.
x = Bar[Foo[int]]().fn()
Чего я не смог понять, так это как аннотировать атрибут Bar
с использованием параметра T
in Bar[Foo[T]]
. Что-то вроде
class Bar(Generic[Foo[T]]): # Still invalid.
item: T
силл не работает как.
Однако это можно обойти, заключив атрибут в свойство
class Bar(Generic[S]):
_item: Any
@property
def item(self: Bar[Foo[T]]) -> T:
return self._item
ценой подсказки типа за _item
неточность.