There are two ways in which this can be done with the new TypeGuard feature, which can be imported from typing in Python 3.10, and is available from the PyPI typing_extensions package in earlier Python versions. Note that typing_extensions is already a dependency of Mypy, so if you're using Mypy, you likely already have it.
The first option is to change your check_a method to a staticmethod that takes in a variable that might be int or None as an input, and verifies whether or not it is an int. (Apologies, I have changed the names of some of your variables, as I found it quite confusing to have a class A that also had an a attribute.)
from typing import TypeGuard, Optional
class Foo1:
def __init__(self, bar: Optional[int] = None) -> None:
self.bar = bar
@staticmethod
def check_bar(bar: Optional[int]) -> TypeGuard[int]:
return bar is not None
f1 = Foo1()
if f1.check_bar(f1.bar):
print(f1.bar + 1)
The second option is to use structural subtyping to assert that an instance of class Foo (or class A in your original question) has certain properties at a certain point in time. This requires altering the test method so that it becomes a classmethod, and is a little more complex to set up, but leads to a nicer check once you have it set up.
from typing import TypeGuard, Optional, Protocol, TypeVar
class HasIntBar(Protocol):
bar: int
F = TypeVar('F', bound='Foo2')
class Foo2:
def __init__(self, bar: Optional[int] = None) -> None:
self.bar = bar
@classmethod
def check_bar(cls: type[F], instance: F) -> TypeGuard[HasIntBar]:
return instance.bar is not None
f2 = Foo2()
if Foo2.check_bar(f2): # could also write this as `if f2.check_bar(f2)`
print(f2.bar + 1)
You can try both of these options out on Mypy playground here.
a.ais notNone. However,mypydoesn't take runtime values into account and from the point of view of a compilera.acould be either anintorNone. AndNone + 1should generate an error.typing.cast?from typing import castprint(cast(int, a.a) + 1)castis for. I don't think there's another way to make the type checker understanda.check_a(); but I could be wrong…self?