← Home

082. Init Constructor

Initialize object state consistently at creation time

082. Init Constructor

He writes the Process class and stores attributes with the wrong names — self.mem instead of self.rss_mb. The summary() method crashes with AttributeError: 'Process' object has no attribute 'rss_mb' every time.

He also skips validating rss_mb in __init__. A negative RSS value (from a buggy /proc parser) slips in silently.

The fix: store attributes with the right names AND validate in __init__:

class Process:
    def __init__(self, name: str, pid: int, rss_mb: float):
        if rss_mb < 0:
            raise ValueError(f"rss_mb cannot be negative: {rss_mb}")
        self.name   = name    # correct names
        self.pid    = pid
        self.rss_mb = rss_mb
        self.alerts = []      # default mutable state initialized here

💡 __init__ is the one guaranteed entry point. Every instance starts with correct, validated state — or it is never created. No “I’ll validate it later” — later never comes.

Learning objectives

  • Define init to initialize all instance attributes
  • Validate parameters inside init
  • Distinguish between init, repr, and str

Key concepts

  • init
  • self
  • instance attributes
  • repr

Try it

Concept detail

init(self, …) is the constructor — Python calls it automatically when you create an instance: Product(“Widget”, 9.99) → init(self, “Widget”, 9.99)

Three jobs of init:

  1. Store parameters as instance attributes with consistent names
  2. Validate inputs and raise early if they are invalid
  3. Initialize computed or default attributes

Example: def init(self, name, price, category=“general”): if price < 0: raise ValueError(f“Price cannot be negative: {price}“) self.name = name # parameter stored with correct attribute name self.price = price self.category = category self.discount = 0.0 # default — not passed in, but always exists

WHY initialize everything in init:

  • If self.discount is first set in apply_discount(), an instance that never calls apply_discount() will crash when any code tries to read self.discount
  • init makes all attributes predictable and explicit

repr vs str: repr → developer-facing string: f“Product({self.name!r}, {self.price})“ str → user-facing string: print(p) uses this If only repr defined, Python uses it for str as well.

!r in f-strings adds quotes around strings: f“{self.name!r}“ → ‘Widget’ (with quotes, like in repr output)

Solution

class Product:
    def __init__(self, name, price, category="general"):
        if price < 0:
            raise ValueError(f"Price cannot be negative: {price}")
        self.name     = name
        self.price    = price
        self.category = category
        self.discount = 0.0

    def apply_discount(self, percent):
        self.discount = percent / 100
        return self.price * (1 - self.discount)

    def __repr__(self):
        return f"Product({self.name!r}, {self.price})"

Tests

def test_product_attributes():
    p = Product("Widget", 9.99)
    assert p.name == "Widget"
    assert p.price == 9.99
    assert p.category == "general"
    assert p.discount == 0.0

def test_product_custom_category():
    p = Product("Gadget", 29.99, "electronics")
    assert p.category == "electronics"

def test_negative_price():
    with pytest.raises(ValueError):
        Product("Bad", -1)

def test_apply_discount_20():
    p = Product("Item", 100.0)
    result = p.apply_discount(20)
    assert result == 80.0

def test_apply_discount_50():
    p = Product("Half", 200.0)
    result = p.apply_discount(50)
    assert result == 100.0

def test_repr_contains_name():
    p = Product("Widget", 9.99)
    r = repr(p)
    assert "Widget" in r
    assert "9.99" in r

Resources