Python Fundamentals contents
Classes and Objects
Model data and behaviour together with classes: attributes, methods, __init__, __str__ and class attributes.
Read first: Functions
Object-oriented programming (OOP) groups data and the functions that operate on it into one unit. A class is the blueprint; an object (or instance) is one thing built from it.
A first class
class Rectangle:
def __init__(self, width, height):
self.width = width # instance attributes
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
r = Rectangle(3, 4)
assert r.area() == 12
assert r.perimeter() == 14
r.width = 10 # attributes can change
assert r.area() == 40__init__is the constructor; Python calls it when you writeRectangle(3, 4).selfis the instance the method is working on. It is passed automatically as the first argument of every method.r.area()is shorthand forRectangle.area(r).
Readable objects: __str__ and __repr__
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self): # unambiguous, for debugging
return f"Point({self.x}, {self.y})"
def __str__(self): # friendly, used by print and str()
return f"({self.x}, {self.y})"
p = Point(1, 2)
assert repr(p) == "Point(1, 2)"
assert str(p) == "(1, 2)"
assert f"{p}" == "(1, 2)"Class attributes
A variable defined in the class body is shared by every instance.
class Counter:
created = 0 # class attribute
def __init__(self):
Counter.created += 1
self.value = 0
def increment(self, by=1):
self.value += by
return self
a, b = Counter(), Counter()
a.increment().increment(5) # returning self allows chaining
assert a.value == 6 and b.value == 0
assert Counter.created == 2Encapsulation
Python has no real private members. By convention a leading underscore (_balance) means "internal, please don't touch". A property lets you expose a computed or validated attribute with normal attribute syntax:
class Account:
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 positive")
self._balance += amount
acc = Account(100)
acc.deposit(50)
assert acc.balance == 150Static and class methods
class Temperature:
@staticmethod
def c_to_f(c): # no self: a plain function that lives in the class
return c * 9 / 5 + 32
@classmethod
def freezing(cls): # receives the class instead of an instance
return cls.c_to_f(0)
assert Temperature.c_to_f(100) == 212
assert Temperature.freezing() == 32When do you need classes in contests?
Rarely. Tuples, lists and dictionaries are enough for most problems, and plain lists are faster. Classes shine when you model something with state and several operations, such as a Fenwick tree or a union-find structure, and they make larger projects maintainable.
dataclasses remove the boilerplate for simple records:
from dataclasses import dataclass
@dataclass(order=True)
class Edge:
weight: int
u: int
v: int
edges = [Edge(5, 0, 1), Edge(2, 1, 2)]
assert min(edges) == Edge(2, 1, 2) # ordering and equality for freeExercises
- Write a
Studentclass with a name and a list of grades, and a method returning the average. - Write a
Stackclass withpush,pop,peekandis_empty. - Write a
Fractionclass that stores a numerator and a denominator in lowest terms. - Add
__len__and__getitem__to a class and observe thatlen(x)andx[i]now work.