Reputation: 1170
I want to create an inheritable class that can be parameterized with a type. Here is a working example without type annotations:
class Simple:
pass
class Bla:
obj_class = Simple
def do(self):
return self.obj_class()
def check(self, x):
return isinstance(x, self.obj_class)
Users of this code would inherit from Bla, and can set a different obj_class, like so:
class Advanced(Simple):
pass
class Foo(Bla):
obj_class = Advanced
The problem starts when I want to correctly type annotate this. I thought of making Bla inherit from Generic[T], where T is defined as TypeVar('T', bound=Simple)
, but then the constructor T() and isinstance won't work, and also manually assigning a different class to obj_class also doesn't work.
Here is one non-working example, as T can't be used in non-typing contexts:
class Simple:
pass
T = TypeVar('T', bound=Simple)
class Bla(Generic[T]):
def do(self) -> T:
return T()
def check(self, x: Any) -> bool:
return isinstance(x, T)
Here is another non-working example, where I can't assign Simple to obj_class because of incompatible types.
class Simple:
pass
T = TypeVar('T', bound=Simple)
class Bla(Generic[T]):
obj_class: Type[T] = Simple
def do(self) -> T:
return self.obj_class()
def check(self, x: Any) -> bool:
return isinstance(x, self.obj_class)
class Advanced(Simple):
pass
class Foo(Bla):
obj_class = Advanced
Is there a way to solve this?
Upvotes: 3
Views: 2331
Reputation: 96
You don't need Type[T] = Simple
.
mypy states:
Incompatible types in assignment (expression has type "Type[Simple]", variable has type "Type[T]").
You are trying to assign a concrete type to a generic type variable.
Instead, do something like:
class Simple:
pass
class Advanced(Simple):
pass
class Other:
pass
T = TypeVar('T', bound=Simple)
class Bla(Generic[T]):
obj_class: Type[T]
def do(self) -> T:
return self.obj_class()
def check(self, x: Any) -> bool:
return isinstance(x, self.obj_class)
class SimpleFoo(Bla[Simple]):
obj_class = Simple
class AdvancedFoo(Bla[Advanced]):
obj_class = Advanced
class OtherFoo(Bla[Other]):
obj_class = Other
Now, mypy correctly states:
error: Type argument "tmp.Other" of "Bla" must be a subtype of "tmp.Simple"
Note
OtherFoo
has to subclass Bla
with a specific type so mypy will correctly warn you.
The following produces no errors:
class OtherFoo(Bla):
obj_class = Other
Upvotes: 8