wot*_*nii 7 oop python-3.x mypy python-3.8 python-typing
当参数可以具有从特定基类型派生的任何类型时,如何注释抽象方法的函数参数的类型?
例子:
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)
Run Code Online (Sandbox Code Playgroud)
当用 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"
Run Code Online (Sandbox Code Playgroud)
我需要如何更改 Base.do_something() 的签名,以便 mypy 不会报告任何错误,同时仍然强制执行抽象方法 do_something 的函数参数是从 BaseConfig 派生的?
Mis*_*agi 10
TLDR:创建基类Generic并参数化配置类型:
C = TypeVar('C', bound=BaseConfig)
class Base(abc.ABC, Generic[C]):
@abc.abstractmethod
def do_something(self, config: C):
pass
Run Code Online (Sandbox Code Playgroud)
原始类层次结构声明ClassA可以在任何地方使用Base是有效的。当我们假设某个变量 时obj: Base,这会导致冲突:
obj = ClassA()因为ClassA“是一个”Base类。obj.do_something(BaseConfig())since obj“is a”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
Run Code Online (Sandbox Code Playgroud)
这使我们能够拥有通用和具体的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)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
730 次 |
| 最近记录: |