Methods: behaviour that belongs to the object
A method is a function defined inside a class. Called on an object, it receives that object as self, so it can compute from its attributes or change them.
- Define methods with
selfas the first parameter and call them asobj.method(...) - Write methods that compute a value from the object's state, and methods that change it
- Keep rules (like "can't ship more than is in stock") inside the methods that change the state
Functions that work on one kind of object belong with it. A method is a function defined inside the class. Its first parameter is self. You call it on an object, order.total(), and Python passes that object as self, so order.total() means "run Order's total with self = this order".
Two kinds of method cover most needs:
- methods that compute a value from the attributes and return it (
total,needs_reorder); - methods that change the attributes (
add_line,ship), often checking a rule first.
A computed method stays right after changes: a total stored once in __init__ goes stale when a line is added; a total() method recalculates every time.
Run each example to see what it prints. Running examples doesn't count toward progress.
class Order: def __init__(self, order_id): self.order_id = order_id self.lines = [] def add_line(self, sku, quantity, unit_price): if quantity < 1: raise ValueError(f"quantity must be at least 1, got {quantity}") self.lines.append((sku, quantity, unit_price)) def total(self): amount = 0 for sku, quantity, unit_price in self.lines: amount += quantity * unit_price return round(amount, 2) order = Order("O100282") order.add_line("SPO-11681", 2, 17.92) print(order.order_id, order.total()) order.add_line("HOM-22847", 1, 4.99) print(len(order.lines), "lines, total", order.total())
class Order: def __init__(self, order_id): self.order_id = order_id self.lines = [] def item_count(): return len(self.lines) order = Order("O100918") print(order.item_count())
class StockItem: def __init__(self, sku, on_hand, reorder_level): self.sku = sku self.on_hand = on_hand self.reorder_level = reorder_level def receive(self, quantity): self.on_hand += quantity def ship(self, quantity): if quantity > self.on_hand: raise ValueError(f"only {self.on_hand} of {self.sku} on hand") self.on_hand -= quantity def needs_reorder(self): return self.on_hand <= self.reorder_level mat = StockItem("SPO-11681", 12, 5) for kind, quantity in [("ship", 4), ("ship", 3), ("receive", 10), ("ship", 15), ("ship", 30)]: try: if kind == "ship": mat.ship(quantity) else: mat.receive(quantity) print(kind, quantity, "-> on hand", mat.on_hand, "| reorder:", mat.needs_reorder()) except ValueError as err: print(kind, quantity, "-> refused:", err)
When to use it: when a function needs one object's attributes, or must keep that object's rules, make it a method of the class.
Mistakes to avoid: forgetting self (above); forgetting the brackets, order.total instead of order.total() (you get the method itself, not its result); storing values that can be computed (they go stale); and changing attributes from outside the class when a method exists to do it safely.
- A method is a function in the class whose first parameter is
self; call it asobj.method(). - Compute values with methods so they stay right after the object changes.
- Changes go through methods that check the object's rules first.
- Next: values that belong to the class itself, and constructors that build objects from other data.