Classes¶
Basic Class Definition¶
class Person:
"""A person with a name and age."""
# Field declarations (required)
name: str
age: int
# Constructor
def __init__(self, name: str, age: int):
self.name = name
self.age = age
# Instance method
def greet(self) -> str:
return f"Hello, I'm {self.name}"
def celebrate_birthday(self):
self.age += 1
Rules:
- All instance fields must be declared at class level with type annotations or an assignment of a default value where the type can be inferred
- Class-body names are not visible by their bare name inside methods — a field is reached through self.name, a const or @static member through ClassName.name. See Class-Body Names Are Not Visible by Bare Name Inside Methods in variable_scoping.md
- The self parameter is required for instance methods
- The self parameter is not type-annotated and cannot be annotated
- Sharpy supports the Self type (inspired by PEP 673) for methods that return the enclosing class type:
Selfresolves to the concrete enclosing class type at compile timeSelfcan be used as a return type or parameter type in methodsSelfis valid only inside class, struct, or interface bodies (SPY0384 outside, SPY0385 in static methods)Selfcan also be used as a parameter type for same-type operations:
class Vector:
x: int
y: int
def add(self, other: Self) -> Self:
return Vector(self.x + other.x, self.y + other.y)
Selfin a field annotation resolves the same way, and keeps its?:
Selfin an interface member means "the implementing type". The interface's own contract is written in terms of the interface, and each implementing class states its own type; the compiler generates the explicit-interface forwarding member that connects the two, so both spellings work at once:
interface IComparable:
def compare_to(self, other: Self) -> int:
...
class Num(IComparable):
v: int
def __init__(self, v: int):
self.v = v
def compare_to(self, other: Self) -> int:
return self.v - other.v
A Self nested inside a type argument (list[Self]) and a generic declaring interface
(IBuilder[T]) are not yet connected this way — see #1342.
__init__return type is implicitlyNoneand can be omitted or explicitly declared
class Person:
name: str
# Both forms are valid and equivalent:
def __init__(self, name: str): # Implicit None return
self.name = name
def __init__(self, name: str) -> None: # Explicit None return
self.name = name
Note: The rule "return type can be omitted for -> None functions" applies universally to all functions and methods, not just __init__. This is consistent with C# where void methods simply have no return type in the signature:
class Counter:
value: int = 0
def increment(self): # Implicit -> None
self.value += 1
def reset(self) -> None: # Explicit -> None (both valid)
self.value = 0
Implementation - ✅ Native - Direct mapping to C# class.