Classes
Now that we know how to type variables and functions, typing classes is easy.
import math
class Point:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def distance(self, other: "Point") -> float: # forward reference
return math.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)
p: Point = Point(0, 0)
Forward References
Notice the quoted type "Point"
in the distance function:
def distance(self, other: "Point") -> float:
pass
This is called a forward reference. Forward references allow us to use types which have not yet been defined.
Class Variables
The line between class and instance variables in Python is a bit blurred. Variables can be typed in one of three ways: as regular variables, pure class variables, or pure instance variables.
Pure class variables can only be set at the class level. Pure instance variables can only be accessed on a class instance. Regular variables can be set at the class level and overridden on an instance.
Here is a quick, contrived example using all three types of class variables:
from typing import ClassVar
class Point:
points: ClassVar[list["Point"]] = [] # pure class
origin: tuple[int, int] = (0, 0) # regular
def __init__(self, y: int, z: int):
# instance only
self.x = z
self.y = y
@classmethod
def add_point(cls, point: "Point"):
cls.points.append(point)
Point.points = []
Point.origin = (0, 0)
Point.x = 3
#> Cannot access member "x" for type "Type[Point]"
#> Member "x" is unknown
point = Point(1, 2)
point.points = []
#> Cannot assign member "points" for type "Point"
point.x = 2
point.y = 3
point.origin = (0, 1)
Point.add_point(point)
The Pyright documentation details this in more depth, though it's usually enough to just know whether you need a pure class variable or a pure instance variable.