Inheritance¶
Single Class Inheritance¶
Sharpy supports single class inheritance only. A class can extend at most one base class but may implement multiple interfaces.
class Employee(Person):
employee_id: str
def __init__(self, name: str, age: int, employee_id: str):
super().__init__(name, age)
self.employee_id = employee_id
def greet(self) -> str:
return f"Hello, I'm {self.name}, employee #{self.employee_id}"
Multiple Class Inheritance is Not Supported:
class A:
pass
class B:
pass
# ❌ ERROR: Multiple class inheritance not allowed
class C(A, B): # ERROR: A class can only extend one base class
pass
# ✅ OK: Single class + multiple interfaces
class C(A, ISerializable, IComparable):
pass
Implementation
- ✅ Native - : BaseClass; super().__init__() → : base() or base.Method()
Multiple Interface Implementation¶
class JSONEmployee(Employee, ISerializable, IComparable):
def serialize(self) -> str:
# Implementation
pass
def compare_to(self, other: object) -> int:
# Implementation
pass
Rules: - Single class inheritance allowed - Multiple interface implementation allowed - Base class (if present) must come first
Implementation
- ✅ Native - : BaseClass, IInterface1, IInterface2
The super() Function¶
super() provides access to methods from a parent class. It is only valid in specific contexts.
Grammar¶
The super() construct has a restricted grammar:
This means:
- super() must be followed by . (member access)
- The member must be a method (identifier)
- The method must be called (parentheses required)
Valid forms:
super().__init__(args) # Constructor call
super().method_name(args) # Method call
super().__eq__(other) # Dunder method call
Invalid forms (syntax/semantic errors):
super() # ERROR: standalone super() not allowed
super().field # ERROR: cannot access fields via super()
super().__init__ # ERROR: must call the method
x = super() # ERROR: cannot assign super()
super().method # ERROR: must call the method
Valid Contexts for super()¶
| Context | Example | Purpose |
|---|---|---|
Inside __init__ |
super().__init__(args) |
Call parent constructor |
| Inside dunder methods | super().__eq__(other) |
Call parent dunder implementation |
Inside @override methods |
super().method() |
Call parent method being overridden |
Constructor Chaining:
Use super().__init__() to call the parent class constructor:
class Animal:
name: str
def __init__(self, name: str):
self.name = name
class Dog(Animal):
breed: str
def __init__(self, name: str, breed: str):
super().__init__(name) # ✅ Call parent constructor
self.breed = breed
Calling Overridden Methods:
When a method overrides a parent method, super() can call the parent implementation:
class Shape:
@virtual
def describe(self) -> str:
return "A shape"
class Circle(Shape):
radius: float
def __init__(self, radius: float):
self.radius = radius
@override
def describe(self) -> str:
base_description = super().describe() # ✅ Calls Shape.describe()
return f"{base_description}: circle with radius {self.radius}"
Inside Dunder Methods:
Within a dunder method, super() can call the parent's dunder implementation. This includes calling the same dunder on the parent, or other dunders for cross-dunder synthesis:
class Child(Parent):
@override
def __repr__(self) -> str:
return super().__repr__() + " (child)" # ✅ Same dunder on parent
@override
def __eq__(self, other: object) -> bool:
if not super().__eq__(other): # ✅ Same dunder on parent
return False
# Additional comparison logic...
return True
def __le__(self, other: Child) -> bool:
# Cross-dunder synthesis (also allowed on self, see Dunder Invocation Rules)
return self.__lt__(other) or self.__eq__(other) # ✅ OK
No chained super() for Multi-Level Inheritance:
In deep inheritance hierarchies, you cannot chain super() calls to access methods from ancestors further up the chain:
class A:
@virtual
def process(self) -> str:
return "A"
class B(A):
@override
def process(self) -> str:
return "B+" + super().process() # Returns "B+A"
class C(B):
@override
def process(self) -> str:
# Access immediate parent (B)
b_result = super().process() # "B+A"
# Access grandparent (A) by chaining
a_result = super().super().process() # ERROR: Not allowed
return f"C({b_result}, {a_result})"
Invalid Contexts:
super() is a compile-time error in any other context:
class Example:
value: int
def __init__(self, value: int):
self.value = value
def regular_method(self) -> None:
super().something() # ❌ ERROR: regular_method does not override a parent method
def another_method(self) -> int:
return super().value # ❌ ERROR: cannot access parent fields via super()
class Standalone:
def method(self) -> None:
super().__init__() # ❌ ERROR: Standalone has no parent class
def free_function():
super().something() # ❌ ERROR: super() not valid outside class methods
Compiler Error Message:
When super() is used in an invalid context, the compiler provides a helpful error:
error: `super()` is only valid in:
- `__init__()` (to call parent constructor)
- dunder methods (to call parent dunders)
- methods decorated with @override (to call overridden methods up the inheritance chain)
Rules Summary:
super()requires the class to have a parent class (compile error otherwise)- In
__init__: can callsuper().__init__(...) - In
@overridemethods: can callsuper().method_name(...)for overridden methods up the chain - In dunder methods: can call
super().__dunder__(...)for parent dunders, andsuper().__other_dunder__(...)for cross-dunder synthesis super()cannot be chained to access ancestors further up the inheritance hierarchy- Cannot use
super()to access parent fields - Cannot use
super()in non-overriding regular methods - Cannot use
super()in free functions or static methods
Implementation
- 🔄 Lowered - super() maps to base.Method()