| Type | Ordered | Mutable | Unique | Use for |
|---|---|---|---|---|
| list | ✓ | ✓ | ✗ | Sequences, stacks |
| tuple | ✓ | ✗ | ✗ | Fixed records, keys |
| dict | ✓ (3.7+) | ✓ | Keys only | Key→value mapping |
| set | ✗ | ✓ | ✓ | Membership tests, dedup |
class Student:
def __init__(self, name, grade): # constructor
# __init__ runs automatically on Student(...)
self.name = name # saved on THIS object
self.grade = grade
def is_passing(self): # instance method
return self.grade >= 50
When you call Student('Alice', 87), Python creates a new object.
Then Python calls __init__(self, ...) to initialise that object.
self means: the current instance being initialised or used.
alice = Student('Alice', 87)
bob = Student('Bob', 42)
print(alice.name, alice.grade) # Alice 87
print(bob.name, bob.grade) # Bob 42
Class variable = shared by all objects from the class.
Instance attribute = stored separately in each object.
class Student:
school = 'UNIGE' # class variable (shared)
def __init__(self, name):
self.name = name # instance attribute (per object)
def label(self):
return f"{self.name} @ {self.__class__.school}"
print(Student.school) # access via class
print(Student('Alice').label()) # access via method/self
Goal: the object protects its own state with controlled access.
class BankAccount:
def __init__(self, balance=0):
self._balance = balance
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount <= 0:
raise ValueError('amount must be > 0')
self._balance += amount
account = BankAccount(100)
account.deposit(50)
account.balance = 50 # this is not allowed and will rais an error
print(account.balance) # 150
Users can read balance, but updates must pass validation in deposit().
Goal: expose a simple interface and hide internal steps.
class CoffeeMachine:
def make_coffee(self):
self._heat_water()
self._grind_beans()
self._brew()
return 'Coffee is ready!'
def _heat_water(self): pass
def _grind_beans(self): pass
def _brew(self): pass
machine = CoffeeMachine()
print(machine.make_coffee())
Caller cares about make_coffee(), not hidden mechanics. He is not aware (or has to care!) about i.e. brew(). Here, you see, internal functions are using a starting '_', which is a pythonic way of saying, this is an internal function call only (it does, however, not enfirce it not being callable)
Goal: reuse shared fields/behaviour, then specialise.
class UniversityMember:
def __init__(self, name, email):
self.name = name
self.email = email
class Student(UniversityMember):
def __init__(self, name, email, student_id):
super().__init__(name, email)
self.student_id = student_id
s = Student('Alice', 'alice@unige.ch', 'S12345')
print(s.name, s.student_id)
Student is-a UniversityMember with extra data.
Goal: same method call, class-specific implementation.
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCard(PaymentMethod):
def pay(self, amount):
return f'Paid {amount} with card'
class PayPal(PaymentMethod):
def pay(self, amount):
return f'Paid {amount} with PayPal'
def checkout(payment_method: PaymentMethod, amount):
print(payment_method.pay(amount))
checkout(CreditCard(), 42)
checkout(PayPal(), 42)
All concrete classes share the same abstract pay() contract.