I am new to static type checking in python and honestly I believed it was much easier that what it actually is.
Here is an ultra simplified version of my code:
from typing import Collection, TypeVar, Generic
ItemType = TypeVar('ItemType')
class WorkerMeta(type):
pass
class Worker(metaclass=WorkerMeta):
def __init__(self) -> None:
self.item: ItemType # error: Type variable "unbound.ItemType" is unbound [valid-type]
# (Hint: Use "Generic[ItemType]" or "Protocol[ItemType]" base class
# to bind "ItemType" inside a class)
# (Hint: Use "ItemType" in function signature to bind "ItemType"
# inside a function)
def get_collection(self) -> Collection[ItemType]:
l : Collection[ItemType] = []
return l
def run(self) -> None:
items: Collection[ItemType] = self.get_collection() # error: Type variable "unbound.ItemType" is unbound [valid-type]
# (Hint: Use "Generic[ItemType]" or "Protocol[ItemType]" base class
# to bind "ItemType" inside a class)
# (Hint: Use "ItemType" in function signature to bind "ItemType"
# inside a function)
for self.item in items:
print(self.item)
class MyWorker(Worker):
def get_collection(self) -> Collection[ItemType]:
return [1,2,3] # error: List item 0 has incompatible type "int"; expected "ItemType" [list-item]
# error: List item 1 has incompatible type "int"; expected "ItemType" [list-item]
# error: List item 2 has incompatible type "int"; expected "ItemType" [list-item]
w = MyWorker()
w.run()
# output will be
# 1
# 2
# 3
The worker class is defining a kind of execution scheme that is then reproduced by all its subclasses.
In the run method there is a loop on a collection made of elements all of the same type, but that might be different in different worker subclasses. I thought this was the task for a TypeVar.
The code works perfectly. But mypy is complaining a lot. I put the error messages as comment in the code.
Can you suggest me how to fix this issue with typing keeping my code still working?
At the very least, your worker class must be generic in the item type in order for ItemType
to be bound to a specific class at runtime. Something like
from typing import Generic, TypeVar
ItemType = TypeVar('ItemType')
class Worker(Generic[ItemType]):
item: ItemType
def __init__(self) -> None:
...
def get_collection(self) -> Collection[ItemType]:
l : Collection[ItemType] = []
return l
def run(self) -> None:
items: Collection[ItemType] = self.get_collection()
for self.item in items:
print(self.item)
Usually, the call to __init__
will fix the value of ItemType
for a particular instance of Worker
: maybe it takes one or more values of type ItemType
, or a list of type list[ItemType]
to use as the initial collection. But you can also fix the type "manually", with something like
w: Worker[int] = Worker()