Skip to content

Variables

Typed declaration

def main() -> None:
x: int = 42
name: str = "hello"
p: Point = Point(1, 2)

Type inference

For simple assignments where the right-hand side has a single concrete type, the annotation is optional:

def main() -> None:
x = 42 # inferred as int
name = "hello" # inferred as str
ratio = 2.5 # inferred as float
ok = True # inferred as bool

Ambiguous cases still require an explicit annotation:

def main() -> None:
xs: list[float] = [] # required — [] alone is ambiguous
val: str | None = None # required — None alone is ambiguous

Reassignment

def main() -> None:
x: int = 1
x = 2

Tuple destructuring

def main() -> None:
pair: (int, str) = (10, "hello")
(n, s) = pair
print(n)
print(s)

Nested destructuring is supported:

def main() -> None:
nested: ((int, float), int) = ((4, 5.5), 6)
((inner_a, inner_b), inner_c): ((int, float), int) = nested
print(inner_a)
print(inner_b)
print(inner_c)

A plain name inside a tuple target can bind a whole tuple subvalue directly:

def main() -> None:
nested: ((int, float), int) = ((4, 5.5), 6)
(whole, c) = nested # whole is (int, float), c is int

Top-level variables

Variables can also be declared at module scope:

seed: int = 7
base = 4
def main() -> None:
print(seed)
print(base)

Scope

Variables declared inside a function are local to that function. Top-level variables are module-scoped. There is no block scope inside if/while/for.