I = TypeVar("I", bound=Optional[Iterable])
O = TypeVar("O", bound=Optional[Mappable])
class Worker(Generic[I, O]):
@abstractmethod
def do_work(self, input: I) -> O:
pass
worker = Worker[list, dict]()
worker_with_optional = Worker[Optional[list], Optional[dict]]()
worker_bad_types = Worker[Optional[list], dict]()
示例中的实际代码可能看起来有点做作,但这是我能想到的抽象表示我的问题的最佳方式。我正在努力做的是为
TypeVars
的输入设置 Generic
,这将允许用户创建 worker
和 worker_with_optional
但 not worker_bad_types
。
我正在努力将其放入代码中的概念是两种输入类型的相关性。我想要求
I
和 O
都是可选的,或者都需要有一个值。
我想要的功能的最佳解决方法是制作该类的两个版本,如下所示:
I = TypeVar("I", bound=Iterable)
O = TypeVar("O", bound=Mappable)
class Worker(Generic[I, O]):
@abstractmethod
def do_work(self, input: I) -> O:
pass
class WorkerWithOptional(Generic[I, O]):
@abstractmethod
def do_work(self, input: Optional[I]) -> Optional[O]:
pass
worker = Worker[list, dict]()
worker_with_optional = WorkerWithOptional[list, dict]()
# worker_bad_types now has a type error because None is not Iterable
worker_bad_types = Worker[Optional[int], str]()
这种方法实现了我想要的类型限制,但我不想为我的类制作两个副本来解决这个问题。有没有办法关联传递到
Generic
的类型的可选性,以便它们始终匹配?
I = TypeVar("I", bound=Iterable)
O = TypeVar("O", bound=Mapping)
T = TypeVar("T", None, Never)
class BaseWorker(Generic[I, O, T]):
@abstractmethod
def do_work(self, input: I | T) -> O | T:
pass
Worker: TypeAlias = BaseWorker[I, O, Never]
WorkerWithOptional: TypeAlias = BaseWorker[I, O, None]
Never
是不可实例化的,这意味着 I | Never
简化为 I
。我们利用这一点来允许您通过参数T
来控制您的选择性。
如果运行以下代码的mypy Playground,您可以看到在 4 个测试用例中,只有 #2 收到预期的类型错误。
class TestWorker(Worker[list[int], dict[int, int]]):
def do_work(self, input: list[int]) -> dict[int, int]:
return {x: 1 for x in input}
class TestWorkerWithOptional(WorkerWithOptional[list[int], dict[int, int]]):
def do_work(self, input: list[int] | None) -> dict[int, int] | None:
return None if input is None else {x: 1 for x in input}
worker = TestWorker()
worker_w_opt = TestWorkerWithOptional()
worker.do_work([1]) # 1
worker.do_work(None) # 2
worker_w_opt.do_work([1]) # 3
worker_w_opt.do_work(None) # 4