How is an instance attribute of the same type as the instance type-hinted?

I am trying to assign a variable to an instance of a class such that the variable is of the same type. I want to use the instance itself in the construction of the variable. In order to be compatible with inheritance, I want to type hint it as Self
rather than the class.
The following works:
class Foo:
var: "Foo"
def bar(self) -> None:
self.var = self
However, var
is marked as being of type Foo
rather than Self
. What I want is:
from typing import Self
class Foo:
var: Self
def bar(self) -> None:
self.var = self
In this case, mypy gives me the following message:
error: Incompatible types in assignment (expression has type "Foo", variable has type "Self") [assignment]
Clearly, self
is not regarded as being of type Self
but only of type Foo
.
Answer
PEP 673 (ref Read more) introduced typing.Self
in Python 3.11 to let you write:
from typing import Self
class Foo:
def clone(self) -> Self:
…
so that in subclasses clone()
is recognized as returning the subclass type. But at the moment mypy only special‐cases Self
in method signatures, not in attribute annotations at the class level. So when you write:
class Foo:
var: Self
def bar(self) -> None:
self.var = self
mypy still treats Self
in var: Self
as “the current class” (i.e. Foo
) and complains when you assign self
(which it types as plain Foo
) to something it thinks is Self
(the unknown late‐bound type). It’s essentially a bug/limitation in mypy’s handling of late‐bound attribute annotations.
The pep has examples using TypeVar. I suggest combining that with generic to create a workaround.
E.g.,
from typing import Generic, TypeVar
T = TypeVar("T", bound="Foo")
class Foo(Generic[T]):
var: T # var is *exactly* whatever subclass type T is
def bar(self: T) -> None:
# now mypy knows `self` is of type T, and var is typed T,
# so this assignment is safe
self.var = self
Alternately, if you want a "quick fix", just annotate var with the class-name as you already did in your OP.
Enjoyed this article?
Check out more content on our blog or follow us on social media.
Browse more articles