I have a generic Base
class that instantiates a class called Factory
. The Factory
class is provided to Base
by Base
‘s subclasses, and then Base
passes the instance to certain subclass methods.
I want the type of Factory
in Base
to depend upon an earlier TypeVar
. Is there a way to express this in Python?
Generic for TypeVar and Iterator (or another generic) of that TypeVar (from a few years ago) says, “no”. Has that changed? If not, is there an alternative way to get the types I’m after (maybe involving Protocol
)?
I tried this:
from typing import Generic, TypeVar
T = TypeVar("T")
class Factory(Generic[T]):
def __init__(self, session: Session) -> None:
self.session = session
def get(self, id: int) -> T | None:
return None
def all(self) -> list[T]:
return []
class Session:
def query(self, query: object) -> object:
pass
F = TypeVar("F", bound=Factory[T])
class Base(Generic[T, F]):
def __init__(self, Factory: type[F]) -> None:
self.Factory = Factory
def doSomething(self, factory: F, thing: T) -> None:
pass
def run(self, session: Session) -> None:
factory = self.Factory(session)
for thing in factory.all():
self.doSomething(factory, thing)
class User:
userId: int
name: str
class UserFactory(Factory[User]):
def login(self, user: User) -> None:
self.session.query('whatever')
class UserThing(Base[User, UserFactory]):
def __init__(self):
super().__init__(UserFactory)
def doSomething(self, factory: UserFactory, user: User) -> None:
factory.login(user)
and get the errors
main.py:19: error: Type variable "__main__.T" is unbound [valid-type]
main.py:19: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
main.py:19: note: (Hint: Use "T" in function signature to bind "T" inside a function)
Found 1 error in 1 file (checked 1 source file)
I’m more familiar with TypeScript, where I was able to express the idea I’m after:
class Factory<T> {
constructor(protected session: Session) {}
get(id: number): T | null {
return null
}
all(): T[] {
return []
}
}
interface Session {
query(query: unknown): unknown
}
abstract class Base<T, F extends Factory<T>> {
constructor(public readonly Factory: { new (session:Session): F }) {
}
abstract doSomething(factory: F, thing: T): void
public run(session: Session) {
const factory = new this.Factory(session)
for (const thing of factory.all()) {
this.doSomething(factory, thing)
}
}
}
interface User {
userId: number
name: string
}
class UserFactory extends Factory<User> {
login(user: User) {
this.session.query('whatever')
}
}
class UserThing extends Base<User, UserFactory> {
constructor() {
super(UserFactory)
}
doSomething(factory: UserFactory, user: User) {
factory.login(user)
}
}
1