当一个抽象方法的参数可以具有从特定基类型派生的任何类型时,我如何注释该参数的类型?

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

当一个抽象方法的函数参数的类型可以是由一个特定的基类型派生出来的任何类型时,我如何注释该参数的类型?

例子:当用myphymethod解析这个参数时,我发现这个参数的类型可以是由特定的基类型衍生出来的任何类型。

import abc
import attr

@attr.s(auto_attribs=True)
class BaseConfig(abc.ABC):
    option_base: str

@attr.s(auto_attribs=True)
class ConfigA(BaseConfig):
    option_a: str

@attr.s(auto_attribs=True)
class ConfigB(BaseConfig):
    option_b: bool


class Base(abc.ABC):
    @abc.abstractmethod
    def do_something(self, config: BaseConfig):
        pass

class ClassA(Base):
    def do_something(self, config: ConfigA):
        # test.py:27: error: Argument 1 of "do_something" is incompatible with supertype "Base"; supertype defines the argument type as "BaseConfig"
        print("option_a: " + config.option_a)

class ClassB(Base):
    def do_something(self, config: ConfigB):
        # test.py:33: error: Argument 1 of "do_something" is incompatible with supertype "Base"; supertype defines the argument type as "BaseConfig"
        print("option_b: " + str(config.option_b))

conf_a = ConfigA(option_a="value_a", option_base="value_base")
conf_b = ConfigB(option_b=True, option_base="value_base")
object_a = ClassA()
object_b = ClassB()
object_a.do_something(conf_a)
object_b.do_something(conf_b)

当我用mypy解析这个时,我得到了

test.py:27: error: Argument 1 of "do_something" is incompatible with supertype "Base"; supertype defines the argument type as "BaseConfig"
test.py:33: error: Argument 1 of "do_something" is incompatible with supertype "Base"; supertype defines the argument type as "BaseConfig"

我需要如何修改Base.do_something()的签名,使mypy不会报告任何错误,同时仍然强制执行抽象方法do_something的函数参数是由BaseConfig派生的?

python-3.x oop mypy python-3.8 python-typing
1个回答
1
投票

TLDR: 让基类 Generic 并对配置的类型进行参数化。

C = TypeVar('C', bound=BaseConfig)

class Base(abc.ABC, Generic[C]):
    @abc.abstractmethod
    def do_something(self, config: C):
        pass

原来的类层次结构声明: ClassA 哪儿都能用 Base 是有效的。当我们假设一些变量 obj: Base,这就导致了冲突。

  • 我们可以分配 obj = ClassA() 自从 ClassA "是一个" Base 类。
  • 我们可以使用 obj.do_something(BaseConfig()) 自从 obj "是一个" Base 例如:

然而, ClassA.do_something(config: ConfigA) 说我们不能两全其美 当下,与类型等价性相矛盾。


相反,我们需要区分"Base 那要 ConfigA", "Base 那要 ConfigB"等等。这是通过参数化 Base 的类型变量。

from typing import Generic, TypeVar

C = TypeVar('C', bound=BaseConfig)      # C "is some" BaseConfig type

class Base(abc.ABC, Generic[C]):        # class takes type variable ...
    @abc.abstractmethod
    def do_something(self, config: C):  # ... and uses it in method signature
        pass

这使得我们可以同时拥有通用和具体的 Base 变体--例如: Base[ConfigA] 是一个"Base 那要 ConfigA". 由此,可以推导出子类采取适当的配置。

class ClassA(Base[ConfigA]):        # set type variable to ConfigA
    def do_something(self, config: ConfigA):
        print("option_a: " + config.option_a)
© www.soinside.com 2019 - 2024. All rights reserved.