python - 将单独的分支组合成公共(public)结构的设计模式

标签 python design-patterns type-hinting mypy python-typing

我有由数据加载器和数据转换器组成的应用程序。每个加载器和每个转换器都是抽象基本加载器和抽象基本转换器的子类,我将在下面的示例中省略它们。具体的加载器和变压器之间存在 1:1 的映射,即知道哪个加载器和变压器属于一起。

假设我们有两个加载器和两个变压器来处理数据

class Data1: ...

class Data2: ...


class Loader1:
    def get_data(self) -> Data1: ...

class Loader2:
    def get_data(self) -> Data2: ...


class Transformer1:
    def transform_data(self, data: Data1) -> None: ...

class Transformer2:
    def transform_data(self, data: Data2) -> None: ...

这些类现在可以组合到应用程序中

class App1:
    Loader = Loader1
    Transformer = Transformer1

class App2:
    Loader = Loader2
    Transformer = Transformer2

有配套工厂

from typing import Union, Type

def make_app(use_app1: bool) -> Union[Type[App1], Type[App2]]:
    if use_app1:
        return App1
    else:
        return App2

这就是我想使用上面的方式

def main(use_app1: bool) -> None:
    app = make_app(use_app1)
    loader = app.Loader()
    data = loader.get_data()
    transformer = app.Transformer()
    transformer.transform_data(data=data)

但是,mypy complains :

error: Argument "data" to "transform_data" of "Transformer1" has incompatible type "Union[Data1, Data2]"; expected "Data1"  [arg-type]
error: Argument "data" to "transform_data" of "Transformer2" has incompatible type "Union[Data1, Data2]"; expected "Data2"  [arg-type]

有什么方法可以让mypy相信分支Loader1 -> Data1 -> Transformer1Loader2 -> Data2 -> Transformer2是分开不会混合?

是否有可用于此用例的替代模式?

最佳答案

好的,这是解决这个问题的第三次尝试。在这次尝试中,我使用抽象协议(protocol)告诉 MyPy,事实上,在许多这些函数中,返回什么特定类型并不重要,只要对象是返回有一定的接口(interface)。

from typing import Type, Protocol, cast


### ABSTRACT INTERFACES ###


class DataProto(Protocol): ...
    

class LoaderProto(Protocol):
    def get_data(self) -> DataProto: ...
    

class TransformerProto(Protocol):
    def transform_data(self, data: DataProto) -> None: ...


class AppProto(Protocol):
    Loader: Type[LoaderProto]
    Transformer: Type[TransformerProto]
    

### CONCRETE IMPLEMENTATIONS ###
    

class Data1: ...


class Data2: ...


class Loader1:
    def get_data(self) -> Data1: ...


class Loader2:
    def get_data(self) -> Data2: ...


class Transformer1:
    def transform_data(self, data: Data1) -> None: ...


class Transformer2:
    def transform_data(self, data: Data2) -> None: ...


class App1:
    Loader = Loader1
    Transformer = Transformer1


class App2:
    Loader = Loader2
    Transformer = Transformer2
    

GenericAppClassType = Type[AppProto]
    

def make_app(use_app1: bool) -> GenericAppClassType:
    if use_app1:
        return cast(GenericAppClassType, App1)
    else:
        return cast(GenericAppClassType, App2)


def main(use_app1: bool) -> None:
    app = make_app(use_app1)
    loader = app.Loader()
    data = loader.get_data()
    transformer = app.Transformer()
    transformer.transform_data(data=data)

在 mypy Playground 上试试 here .

关于python - 将单独的分支组合成公共(public)结构的设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68812899/

相关文章:

python - 从 multiprocessing.process for a loop 获取结果

python - 自定义指标的 Xgboost Early.stop.round 错误

python - 返回单元素 Numpy 数组的简洁方法

java - Builder Design Pattern 为具有大量参数的方法创建通用方法

c# - 如何设计这样的项目?

c# - 如果使用接口(interface),类是否应该始终严格实现接口(interface)

python - "hashable"的类型提示

python - 类的任何子类的实例的类型提示

python - 如何暗示一个变量是一个继承自另一个类的类?

python - 捕获并立即引发异常时避免 "During handling of the above exception, another exception occurred"