当参数可以具有从特定基类型派生的任何类型时,如何注释抽象方法的参数类型? [英] How do I annotate the type of a parameter of an abstractmethod, when the parameter can have any type derived from a specific base type?

查看:77
本文介绍了当参数可以具有从特定基类型派生的任何类型时,如何注释抽象方法的参数类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How do I annotate the type of a function parameter of a abstractmethod, when the parameter can have any type derived from a specific base type?

示例:

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 解析时我得到

When parsing this with mypy I get

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 派生的?

How would I need to change the signature of Base.do_something() so mypy doesn't report any error, while still enforcing, that the function parameter of the abstract method do_something is derived from BaseConfig?

推荐答案

TLDR:使基类 Generic 并参数化配置类型:

TLDR: Make the baseclass Generic and parameterise the type of configuration:

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

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

<小时>

原始类层次结构声明ClassA 可以在任何Base 有效的地方使用.当我们假设某个变量 obj: Base 时,这会导致冲突:


The original class hierarchy declares that ClassA can be used anywhere Base is valid. When we assume some variable obj: Base, this leads to a conflict:

  • 我们可以赋值 obj = ClassA() 因为 ClassA 是一个" Base 类.
  • 我们可以使用 obj.do_something(BaseConfig()) 因为 obj 是一个" Base 实例.
  • We can assign obj = ClassA() since ClassA "is a" Base class.
  • We can use obj.do_something(BaseConfig()) since obj "is a" Base instance.

但是,ClassA.do_something(config: ConfigA) 说我们不能同时做这两个事情,这与类型等效性相矛盾.

However, ClassA.do_something(config: ConfigA) says we cannot do both at the same time, contradicting the type equivalence.

相反,我们需要区分采用 ConfigABase"和采用 ConfigBBase"代码>"等.这是通过使用配置的类型变量参数化 Base 来完成的.

Instead, we need to distinguish between "Base that takes a ConfigA", "Base that takes a ConfigB" and so on. This is done by parameterising Base with a type-variable for the config.

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".由此,子类可以派生为采用适当的配置:

This allows us to have both generic and concrete Base variants - for example, Base[ConfigA] is a "Base that takes a ConfigA". From this, the subclasses can be derived as taking the appropriate configuration:

class ClassA(Base[ConfigA]):        # set type variable to ConfigA
    def do_something(self, config: ConfigA):
        print("option_a: " + config.option_a)

这篇关于当参数可以具有从特定基类型派生的任何类型时,如何注释抽象方法的参数类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆