The title might be a bit ambiguous as I'm not sure how to phrase what I am trying to do exactly.
I would like to replicate something like this pattern from C++ in 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();
};
In terms of python, I would like to specialize a generic type Bar[T]
with another specialized type Foo[T]
and then use the type that was used to specialize Foo
to type hint in Bar
.
Something like
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()
I know that this relationship can be deduced when creating instances of Bar
if we have something like
class Bar(Generic[T]):
def __init__(self, foo: Foo[T]):
...
but that doesn't really fit my current problem.
I would rather like to be able to create a family of specializations Foo[T]
, Bar[T]
, Baz[T]
, etc without having repeat T
many times. In my real use case the types are more like Foo[R, S, T]
and repeating the types is quite tedious and error prone and, conceptually, classes Bar
, Baz
, etc are thought of not as depending on a type T
but rather a particular type of Foo[T]
.
So it would be nice to have the ability to do something like
MyFoo = Foo[int]
MyBar = Bar[MyFoo]
MyBaz = Baz[MyFoo]
Reading the mypy documentation, I came across this: Advanced uses of self-types
By using a TypeVar
bounded by Foo
in Bar
we can specify that Bar
is parameterized by something Foo
-like. Then, within individual methods we can annotate self
with the nested type Bar[Foo[T]]
causing T
to be bound to the parameter of the parameterizing Foo
parameterizing 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()
What I have been unable to figure out is how to annotate an attribute of Bar
using the parameter T
in Bar[Foo[T]]
. Something like
class Bar(Generic[Foo[T]]): # Still invalid.
item: T
sill doesn't work as.
However, this can be worked around by wrapping the attribute in a property
class Bar(Generic[S]):
_item: Any
@property
def item(self: Bar[Foo[T]]) -> T:
return self._item
at the cost of the type hint for _item
being imprecise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With