耦合两种类型的可选性

问题描述 投票:0回答:1

示例代码

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
的类型的可选性,以便它们始终匹配?

python mypy python-typing
1个回答
1
投票
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
© www.soinside.com 2019 - 2024. All rights reserved.