如何检查具体方法是否尊重抽象方法的类型提示

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

这是一个分为两部分的问题,但是第二部分取决于第一部分。

出于教育目的,我正在尝试为groups(抽象代数的概念)实现抽象基类和测试套件。代数组的部分定义等效于类型约束,我想在ABC上实现该类型约束,并且如果具体类上的方法不符合该约束,就会有些抱怨。

我已经为逻辑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是我发现的only东西,可以注意到这种差异。默认情况下,mypy对此事件保持沉默,与flake8和pylint一样。 PyCharm发挥作用非常好,但是如果我想将其整合到工作流中,那么如果我的具体方法不符合抽象签名,那么我将不得不运行哪些命令会失败?

python type-hinting mypy abstract-algebra finite-group-theory
1个回答
1
投票

第一提示:如果mypy告诉您的信息不足,请尝试mypy --strict

您正确地意识到基类中op的类型注释不够严格,实际上与子类不兼容。

看看这个不工作示例。

from __future__ import annotations
from abc import ABC, abstractmethod


class AbsGroup(ABC):
    @abstractmethod
    def op(self, other: AbsGroup) -> AbsGroup:
        pass


class Bool(AbsGroup):
    def __init__(self, val: str = "False") -> None:
        self.val = val

    def op(self, other: Bool) -> Bool:
        ...

我用正确的类型注释了op中的Bool,但现在mypy抱怨了:

file.py:15:错误:“ op”的参数1与超类型不兼容“ AbsGroup”;超类型将参数类型定义为“ AbsGroup”

[您有两种选择:要么使基本注释的限制更少(Any),要么使您的类成为Generic一个:

from __future__ import annotations
from abc import ABC, abstractmethod

from typing import TypeVar, Generic


T = TypeVar('T')


class AbsGroup(Generic[T], ABC):
    @abstractmethod
    def op(self, other: T) -> T:
        pass

# EDIT: ADDED QUOTES AROUND Bool
class Bool(AbsGroup['Bool']):
    def __init__(self, val: str = "False") -> None:
        self.val = val

    def op(self, other: Bool) -> Bool:
        ...

这涉及几个步骤:

  1. 创建类型变量T(与其他语言的通用类型变量相似)
  2. 让基类也继承自Generic[T],使其成为通用类
  3. 更改op方法以采用并返回T
  4. 让子类从AbsGroup[Bool]继承(在C ++中,它称为CRTP

这将使mypy --strict静音,并且PyCharm会正确推断出op的返回类型。

编辑:

先前的子类定义看起来像class Bool(AbsGroup[Bool]): ... 引号。但这不起作用,并且在创建类时会抛出NameError

NameError:未定义名称'Bool'

这是预期的行为,如PEP 563中所写。

[...]但是,类型模块中有一些API使用该语言的其他语法构造,并且这些API仍需要使用字符串文字解决forward reference。该列表包括:[...]

  • 基类:

    class C(Tuple['<type>', '<type>']): ...

因此,即使我们使用了将来的导入方式,在这种情况下仍然需要使用引号。

仅需注意:为什么对布尔值使用字符串符号?已经有两个完美的工作实例,分别为TrueFalse。这将使您的代码更加简单。例如。可以将构造函数中的检查简化为if type(val) is bool(由于您不希望isinstance成为自定义类型,因此我在这里不使用val。)>

© www.soinside.com 2019 - 2024. All rights reserved.