这是一个由两部分组成的问题,但第二部分依赖于第一部分。
出于教育目的,我试图为 组 实现一个抽象基类和测试套件(概念来自抽象代数)。代数群定义的一部分相当于类型约束,我想在 ABC 上实现该类型约束,并且如果具体类上的方法不符合该约束,就会有一些抱怨。
I对于逻辑
and
下的布尔值组已经有一个首次实现,但它至少有两个问题,我希望你能帮我修复它。
from __future__ import annotations
from abc import ABC, abstractmethod
class AbsGroup(ABC):
@abstractmethod
def op(self, other: AbsGroup) -> AbsGroup: # <-- Line-of-interest #1
pass
class Bool(AbsGroup):
def __init__(self, val="False"):
if val not in ["True", "False"]:
raise ValueError("Invalid Bool value %s" % val)
self.val = val
def op(self, other):
"""Logical AND"""
if self.val == "True" and other.val == "True": # <-- Line-of-interest #2
return Bool("True")
return Bool("False")
def __eq__(self, other):
return self.val == other.val
def __repr__(self):
return self.val
首先:兴趣线 #1 正在执行类型约束工作,但当前的实现是错误的。它仅检查该方法是否接收并返回
AbsGroup
实例。这可以是任何
AbsGroup
实例。我希望它检查它所继承的具体类,它接收并返回该具体类的实例(因此在
Bool
的情况下,它接收并返回
Bool
的实例)。该练习的重点是在一个位置执行此操作,而不是必须在每个具体类上专门设置它。我认为这是通过一些类型提示泛型完成的,这些泛型比我尚未深入研究的类型提示更深一些。我该怎么做?
第二:我如何
检查
具体方法是否符合抽象类型提示?我的 IDE (PyCharm) 中的类型检查器在感兴趣线 #2 处抱怨,因为它期望
other
为
AbsGroup
类型,而该类型没有
val
属性。这是预料之中的,如果我能找到第一个问题的解决方案,这种情况就会消失,但我的 IDE 是我能找到的
唯一
注意到这种差异的东西。
mypy
对此事保持沉默默认,flake8 和 pylint 也是如此。 PyCharm 的功能很棒,但如果我想将其合并到工作流程中,我必须运行什么命令,如果我的具体方法不符合抽象签名,该命令会失败?
正在寻找的是 Python 中的
泛型自引用类型
。这使能够注释
AbsGroup
类,以便类型提示知道每个具体组在操作中使用其自己的类型。
以下是经过更正的代码:
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TypeVar
T = TypeVar('T', bound='AbsGroup') # 定义一个类型变量 'T',它是 'AbsGroup' 或其子类型
class AbsGroup(ABC):
@abstractmethod
def op(self: T, other: T) -> T: # 使用 'T' 进行类型提示
pass
class Bool(AbsGroup):
def __init__(self, val: str = "False"):
if val not in ["True", "False"]:
raise ValueError("Invalid Bool value %s" % val)
self.val = val
def op(self, other: 'Bool') -> 'Bool': # 指定 'other' 和返回类型为 'Bool'
"""Logical AND"""
if self.val == "True" and other.val == "True":
return Bool("True")
return Bool("False")
def __eq__(self, other: 'Bool') -> bool: # 使用 'Bool' 类型提示
return self.val == other.val
def __repr__(self) -> str:
return self.val
说明:
-
TypeVar
: 我们从typing
模块中导入TypeVar
。T = TypeVar('T', bound='AbsGroup')
创建一个类型变量T
,它表示“AbsGroup
或其子类型”。 -
op
方法中的T
: 在AbsGroup
的op
方法中,我们现在使用self: T, other: T) -> T
。这意味着: -
self
的类型为T
-
other
参数也必须是类型T
-
该方法返回类型
T
-
具体类中的类型提示 : 在
Bool
类中,我们仍然提供更具体的类型提示,例如other: 'Bool'
。但这不再是绝对必要的,因为T
会处理这个问题,并且 IDE 和类型检查器能够推断出这一点。
类型检查:
提到 mypy 没有发现问题。可以通过运行以下命令使用 mypy 检查类型提示:
mypy your_file.py
使用更正后的代码,mypy 应该不会报告任何错误。
对于 IDE(如 PyCharm),它通常应该能够识别泛型自引用类型并提供正确的类型检查。如果 PyCharm 仍然显示错误,请确保使用的是最新版本并已启用类型提示。
其他提示:
- 考虑使用枚举来表示布尔值,而不是字符串。这将使代码更安全、更易读。
-
探索使用
@dataclass
装饰器来简化Bool
类的定义。
我希望这些解释和更正后的代码能够帮助理解如何在 Python 中使用抽象基类和类型提示来实现代数群的概念!
标签:python,python-typing,mypy From: 58694760