← Home

083. Instance Methods

Add behavior to objects through methods

083. Instance Methods

He has a Process class with attributes. But the reporting code still lives outside as standalone functions: format_process(p), is_heavy(p, threshold). They take p as an argument — which is the Process itself.

He moves these as methods:

class Process:
    def __init__(self, name, pid, rss_mb):
        self.name   = name
        self.pid    = pid
        self.rss_mb = rss_mb
        self.alerts = []

    def is_heavy(self, threshold_mb=500):
        # self already IS the process — no argument needed
        return self.rss_mb > threshold_mb

    def add_alert(self, message):
        self.alerts.append(message)

    def alert_count(self):
        return len(self.alerts)

    def summary(self):
        flag = " [HEAVY]" if self.is_heavy() else ""
        return f"{self.name}[{self.pid}]: {self.rss_mb} MB{flag}"

chrome = Process("chrome", 1234, 620)
chrome.add_alert("RAM > 500 MB")
print(chrome.summary())     # chrome[1234]: 620 MB [HEAVY]
print(chrome.alert_count()) # 1

💡 Fun fact: When you call chrome.summary(), Python internally translates this to Process.summary(chrome) — passing the instance as the first argument automatically. This is why self must always be the first parameter: Python is literally passing the object to its own method. You can actually call it this “unbound” way manually: Process.summary(chrome) works identically.

⚠️ Watch out: The most common instance method mistake is referencing balance instead of self.balance inside a method. Without self., Python looks for a local variable named balance — which doesn’t exist — and raises UnboundLocalError. The self. prefix is not optional boilerplate; it’s the only way to reach instance state.

🤔 Think about it: Instance methods receive self implicitly, but Python also has @staticmethod (no self) and @classmethod (receives the class, not an instance). When would you choose a static method over a standalone module-level function? What does the choice communicate to readers of your code?

Learning objectives

  • Define instance methods that read and modify attributes
  • Call methods on instances
  • Implement methods that return computed values

Key concepts

  • instance methods
  • self
  • method calls
  • state mutation

Try it

Concept detail

Instance methods are functions defined inside a class that receive self as the first parameter — giving them access to the instance’s attributes.

class TaskList:
    def add(self, title):          # called as tl.add("Buy milk")
        self.tasks.append(...)     # Python passes tl as self automatically

    def pending_count(self):       # called as tl.pending_count()
        return sum(...)

self is implicit: tl.add(“Buy milk”) passes tl as self behind the scenes. You never pass self explicitly when calling a method.

Methods can:

  • Read instance attributes (self.tasks)
  • Modify instance attributes (self.tasks.append(…))
  • Return computed values (return sum(…))
  • Call other methods on self (self.add(“x”) inside another method)

WHY methods instead of standalone functions: # Standalone function — must pass the object: def pending_count(tl): return sum(1 for t in tl.tasks if not t[“done”])

# Method — self already IS the object:
def pending_count(self):
    return sum(1 for t in self.tasks if not t["done"])

Methods are the “verbs” of a class — what the object can DO. Attributes are the “nouns” — what the object HAS.

Solution

class TaskList:
    def __init__(self):
        self.tasks = []

    def add(self, title):
        self.tasks.append({"title": title, "done": False})

    def complete(self, title):
        for task in self.tasks:
            if task["title"] == title:
                task["done"] = True
                return True
        return False

    def pending_count(self):
        return sum(1 for t in self.tasks if not t["done"])

    def summary(self):
        done = sum(1 for t in self.tasks if t["done"])
        return f"{done}/{len(self.tasks)} tasks done"

Tests

def test_add_task():
    tl = TaskList()
    tl.add("Buy milk")
    assert len(tl.tasks) == 1
    assert tl.tasks[0]["title"] == "Buy milk"
    assert tl.tasks[0]["done"] == False

def test_complete_task():
    tl = TaskList()
    tl.add("Read docs")
    result = tl.complete("Read docs")
    assert result == True
    assert tl.tasks[0]["done"] == True

def test_complete_missing():
    tl = TaskList()
    tl.add("Buy milk")
    assert tl.complete("Nonexistent") == False

def test_pending_count():
    tl = TaskList()
    tl.add("A")
    tl.add("B")
    tl.add("C")
    tl.complete("A")
    assert tl.pending_count() == 2

def test_pending_count_all_done():
    tl = TaskList()
    tl.add("X")
    tl.complete("X")
    assert tl.pending_count() == 0

def test_summary():
    tl = TaskList()
    tl.add("X")
    tl.add("Y")
    tl.complete("X")
    assert tl.summary() == "1/2 tasks done"

Resources