I have a dataclass that has a field that might be a constant or might be a function taking Self
. There's a helper function that just does the right thing -- if the field contains a constant, it returns the constant. If the field contains a function, it calls the function using self
:
from dataclasses import dataclass
from typing import Self, Callable
@dataclass
class MyClass:
my_func_field: str | Callable[[Self], str]
def my_field(self) -> str:
if isinstance(self.my_func_field, str):
return self.my_func_field
else:
return self.my_func_field(self)
mypy doesn't like this (playground):
main.py:12: error: Argument 1 has incompatible type "MyClass"; expected "Self" [arg-type]
However, if I simplify the situation so it's just a callable, I don't run into this problem:
from dataclasses import dataclass
from typing import Self, Callable
@dataclass
class MyClass:
my_func_field: Callable[[Self], str]
def my_field(self) -> str:
return self.my_func_field(self)
This is surprising to me. I assumed that the else
branch in the first example would type-check exactly the same as the second example, because in the else
branch, my_func_field
has been narrowed to Callable[[Self], str]
, which is exactly the same type as the second example.
What am I missing here? Is it possible to get mypy to accept something, or do I have to use an # ignore
comment?
Here's at least one workaround using string types instead of Self
.
from dataclasses import dataclass
from typing import Self, Callable
@dataclass
class MyClass:
my_func_field: str | Callable[["MyClass"], str]
def my_field(self) -> str:
if isinstance(self.my_func_field, str):
return self.my_func_field
else:
return self.my_func_field(self)