Objects & classes: state and behaviour move in together
420-302-VA · WEEK 6 · FALL 2026

Utility · use it before asking for help

Troubleshoot by layer

Classes add their own family of errors, and the good news is that almost all of them announce themselves with one of about six exact messages. Learn the messages once and OOP debugging becomes pattern-matching.

Find the layer that failed

Four layers in order: the design assigns state to the right class, the class definition is written correctly, the instances are used correctly, and the runtime layers from Weeks 4 and 5 hold ADesignright class owns the state BDefinitionthe class body is right CUseconstruction and calls DRuntimeWeeks 4 and 5 ladders
Work left to right. "The counter is on the wrong class" is A. "The method body is wrong" is B. "The class is fine, the call is wrong" is C. Everything Python-and-wiring beneath is D: Week 4's ladder for tracebacks, environments and circuits, Week 5's for spec and logic faults, both unchanged.

Symptom table

Messages quoted as Python prints them; match yours, then apply the fix. Bottom line of the traceback first, as always.

Symptom / exact messageLayerMost likely causeWhat to do
TypeError: ReactionLog.add() takes 1 positional argument but 2 were givenBdef add(ms):, the method's self is missing, so your one argument lands in its slot plus oneFirst parameter of every method is self: def add(self, ms):
NameError: name 'times' is not defined, inside a methodBAttribute used without its self. prefixself.times; bare names are locals, and this one does not exist
AttributeError: 'ReactionLog' object has no attribute 'times'B__init__ misspelled (one underscore, _init_, or __int__), so it never ran; or the attribute is created only on some pathsExactly __init__, two underscores each side; create every attribute there, unconditionally
TypeError: __init__() missing 1 required positional argument: 'player'CConstructed without the facts the blueprint demands: ReactionLog()Pass them: ReactionLog("Sam"); help(ReactionLog) lists what construction needs
Prints <bound method ReactionLog.best of ...> instead of a numberCMethod referenced, not called: log.best without parentheseslog.best(); parentheses are the "do it" operator
Button pressed, nothing ever happens, no error at allCwhen_pressed = self.press() with parentheses: ran once at construction, assigned None as the callbackAssign the method itself: when_pressed = self.press
NameError: name 'ReactionLog' is not definedCForgot the import from your module, or used the class above its definition in the same filefrom reactionlog import ReactionLog, or define before use
ModuleNotFoundError: No module named 'reactionlog'CFile name and import name differ, or Python launched from another directoryImport names the file minus .py, exactly; cd ~/iot/week6 first, ls to confirm
Two instances mysteriously share one list of timesBThe attribute was created in the class body, not in __init__, so it belongs to the blueprintSee the shared-state trap below
Mode says one thing, hardware shows anotherASome code path changes state outside the method that owns the rulesRoute every change through the transition method; the encapsulation section is the design fix
Everything constructs, logic just wrongDNot an OOP fault at allWeek 5's trace table on the failing method, then its ladder; a method traces like any function, with a self column

The shared-state trap

The one class bug that produces no message, only wrong behaviour, so it gets its own section:

# WRONG: the list lives on the blueprint, shared by every instance
class ReactionLog:
    times = []
    def add(self, ms):
        self.times.append(ms)

# RIGHT: __init__ gives each instance its own list
class ReactionLog:
    def __init__(self):
        self.times = []

In the wrong version, sam.add(431) shows up in ana.times too: a name assigned directly in the class body is a class attribute, one object attached to the blueprint itself, which every instance sees. Class attributes have honest uses (a constant shared by design), but mutable per-instance state is never one of them. The rule that prevents the trap outright: every attribute an instance owns is created in __init__, through self. The two-players ladder rung is where this bug loves to appear; now it cannot.

Before you ask for help

  • Which layer (A to D) you got stuck at, and why you ruled out the ones to its left.
  • For A: the capsule sketch, and which state has no clear owner.
  • For B: the class definition, plus the exact message's bottom line.
  • For C: the constructing or calling line, side by side with what help(YourClass) says it wants.
  • For D: the trace table or the evidence Week 4's list asks for.