Python Programming

Object-Oriented Python

40 min

Classes and objects

A class bundles data and the behaviour that works on it.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be positive")
        self.balance += amount

    def __repr__(self):
        return f"BankAccount({self.owner!r}, {self.balance})"

acct = BankAccount("Ada")
acct.deposit(50)
print(acct)        # BankAccount('Ada', 50)

self is the object the method was called on. __init__ sets up a new object.

Properties: computed or protected attributes

class Product:
    def __init__(self, price):
        self.price = price

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("price cannot be negative")
        self._price = value

Class and static methods

class User:
    def __init__(self, email): self.email = email

    @classmethod
    def from_dict(cls, data):
        return cls(data["email"])

    @staticmethod
    def is_valid_email(text):
        return "@" in text

Inheritance

class SavingsAccount(BankAccount):
    def __init__(self, owner, balance=0, rate=0.02):
        super().__init__(owner, balance)
        self.rate = rate

    def add_interest(self):
        self.deposit(self.balance * self.rate)

Prefer composition (an object holding other objects) over deep inheritance trees. Two levels is usually plenty.

Dataclasses: less boilerplate

from dataclasses import dataclass, field

@dataclass
class OrderItem:
    sku: str
    qty: int
    price: float

    @property
    def total(self):
        return self.qty * self.price

@dataclass
class Order:
    id: int
    items: list[OrderItem] = field(default_factory=list)

    def total(self):
        return sum(i.total for i in self.items)

Dataclasses generate __init__, __repr__ and __eq__ for you. Use @dataclass(frozen=True) for immutable values.

Special methods

__len__, __iter__, __eq__, __lt__, __str__ let your objects work with len(), for, ==, sorted() and print().

When to use classes

Use a class when data and behaviour belong together and you have several instances (accounts, orders, users), or when a framework expects them (SQLAlchemy models, Flask-WTF forms). For a one-off calculation, a function is simpler.

Exercise

Model a shopping cart: Product (validated price), CartItem and Cart with add, remove, total and apply_coupon(code). Use dataclasses where they fit.

Check yourself

  • What is self?
  • When is a @classmethod useful?
  • Why prefer composition over deep inheritance?

Votre progression est enregistrée dans ce navigateur uniquement.

Envoyer pour relecture (10 points)