从抽象超类枚举 Action
和 Activity
有继承的枚举:
ActivityA
可以在其上执行 ActionA
;和ActivityB
可以对其执行 ActionB
。如何在抽象 action
方法的 perform
方法的 Activity
参数中添加类型声明,以便 MyPy 尊重继承或哪个操作适用于哪个活动?
from abc import ABC, ABCMeta, abstractmethod
from enum import EnumMeta, IntEnum
class ABCEnumMeta(EnumMeta, ABCMeta):
...
class Action(ABC, IntEnum, metaclass=ABCEnumMeta):
...
class ActionA(Action):
start = 1
stop = 2
class ActionB(Action):
start = 1
pause = 2
resume = 3
complete = 4
fail = 5
class Activity(ABC, IntEnum, metaclass=ABCEnumMeta):
@abstractmethod
def perform(
self: "Activity",
action, # <- This line
) -> str:
...
class ActivityA(Activity):
this = 1
that = 2
def perform(
self: "ActivityA",
action: ActionA,
) -> str:
return f"A: {action.name} {self.name}"
class ActivityB(Activity):
something = 1
another = 2
def perform(
self: "ActivityB",
action: ActionB,
) -> str:
return f"B: {action.name} {self.name}"
print( ActivityB.something.perform(ActionB.pause) )
print( ActivityA.this.perform(ActionA.stop) )
print( ActivityB.another.perform(ActionA.start) )
print( ActivityA.that.perform(ActionB.fail) )
正在使用的 mypy.ini
设置文件是:
[mypy]
disallow_any_expr = True
disallow_any_decorated = True
disallow_any_explicit = True
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
MyPy 的输出是:
<块引用>test_enum.py:26: error: Function is missing a type annotation for one or more arguments
test_enum.py:26: error: Type of decorated function contains type "Any" ("Callable[[Activity, Any], str]")
test_enum.py:56: error: Argument 1 to "perform" of "ActivityB" has incompatible type "ActionA"; expected "ActionB"
test_enum.py:57: error: Argument 1 to "perform" of "ActivityA" has incompatible type "ActionB"; expected "ActionA"
(最后两个错误是预期的。)
在这种情况下,我通常会使用泛型并定义:
A = TypeVar("A", bound=Action)
class Activity(ABC, Generic[A], IntEnum, metaclass=ABCEnumMeta):
@abstractmethod
def perform(
self: "Activity",
action A,
) -> str:
...
class ActivityA(Activity[ActionA]):
...
class ActivityB(Activity[ActionB]):
...
但是,当您尝试添加泛型时,Enum
类会引发异常。
如何解决这个问题以正确定义抽象方法的参数类型?
答案 0 :(得分:0)
部分解决方案(因为 Enum
不能有 Generic
混合)是在抽象父方法中使用超类型 Action
(而不是尝试使用子通过泛型类型):
class Activity(ABC, IntEnum, metaclass=ABCEnumMeta):
@abstractmethod
def perform(
self: "Activity",
action: Action,
) -> str:
...
这将给出错误:
<块引用>test_enum.py:36: error: Argument 1 of "perform" is incompatible with supertype "Activity"; supertype defines the argument type as "Action"
test_enum.py:36: note: This violates the Liskov substitution principle
test_enum.py:36: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
test_enum.py:47: error: Argument 1 of "perform" is incompatible with supertype "Activity"; supertype defines the argument type as "Action"
test_enum.py:47: note: This violates the Liskov substitution principle
test_enum.py:47: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
test_enum.py:56: error: Argument 1 to "perform" of "ActivityB" has incompatible type "ActionA"; expected "ActionB"
test_enum.py:57: error: Argument 1 to "perform" of "ActivityA" has incompatible type "ActionB"; expected "ActionA"
可以使用 # type: ignore[override]
显式 locally silenced 附加错误:
class ActivityA(Activity):
this = 1
that = 2
def perform( # type: ignore[override]
self: "ActivityA",
action: ActionA,
) -> str:
return f"A: {action.name} {self.name}"
class ActivityB(Activity):
something = 1
another = 2
def perform( # type: ignore[override]
self: "ActivityB",
action: ActionB,
) -> str:
return f"B: {action.name} {self.name}"
一旦这些错误在本地被消除,那么输出就是两个预期的错误:
<块引用>test_enum.py:56: error: Argument 1 to "perform" of "ActivityB" has incompatible type "ActionA"; expected "ActionB"
test_enum.py:57: error: Argument 1 to "perform" of "ActivityA" has incompatible type "ActionB"; expected "ActionA"
虽然这清楚地表明抽象父方法应该在参数中采用 Action
类型,但它并没有从抽象父类中表明子类应该期待一个特定的子类型(作为泛型会允许)。因此,这只是部分解决方案;更好的解决方案是“修复” Enum
类(或子类 Enum
)以允许 Generic
混合。