This is a two-part question, but the second part is dependent on the first part.
For educational purposes, I am trying to implement an abstract base class and test suite for groups (the concept from abstract algebra). Part of the definition of an algebraic group is equivalent to a type constraint, and I want to implement that type constraint on an ABC, and have something complain if the methods on the concrete classes don't conform to that constraint.
I've got a first-pass implementation for this for the group of Boolean values under logical and
, but there are at least two things wrong with it, and I'm hoping you can help me fix it.
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
Firstly: Line-of-interest #1 is what's doing the type-constraint work, but the current implementation is wrong. It only checks that the method receives and returns an AbsGroup
instance. This could be any AbsGroup
instance. I want it to check that for the concrete class it gets inherited by, it receives and returns an instance of that concrete class (so in the case of Bool
it receives and returns an instance of Bool
). The point of the exercise is to do this in one location, rather than having to set it specifically on each concrete class. I presume this is done with some type-hinting generics that are a little bit deeper than I've yet to delve with regard to type-hinting. How do I do this?
Secondly: how do I check the concrete method is complying with the abstract type hint? The type inspector in my IDE (PyCharm) complains at Line-of-interest #2, because it's expecting other
to be of type AbsGroup
, which doesn't have a val
attribute. This is expected, and would go away if I could figure out the solution to the first problem, but my IDE is the only thing I can find that notices this discrepancy. mypy
is silent on the matter by default, as are flake8 and pylint. It's great that PyCharm is on the ball, but if I wanted to incorporate this into a workflow, what command would I have to run that would fail in the event of my concrete method not complying with the abstract signature?