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

Stage 2 of 6 · Theory, REPL open · about 25 minutes

Objects you own

Before writing a class, read the ones you have been living with. Everything on this page you have already typed; the point is to see the machinery behind it, so that on the next page, when you build the machinery yourself, nothing is new except the keyword.

The capsule: state plus behaviour

An object bundles two things behind one name:

An object drawn as a capsule. Inside, the state: pin 17 and is_lit False. On the capsule's wall, the methods on, off and blink, drawn as the only doors. Outside code reaches the state only through the doors. the object behind the name led state (attributes) pin: 17 is_lit: False on() off() blink() behaviour (methods): the doors, and the only doors your code led.on()
State inside, methods as the doors. Outside code asks the object to act; the object's own methods are what touch its state. That containment, encapsulation, is why a well-built object cannot be put into a nonsense state by a stray line elsewhere.

The capsule picture explains a Week 4 mystery in one stroke: how led.on() "knew" which pin to drive. The pin is state inside the object; you told it once, at construction, and every method since has read it from there. No globals, no repetition, no way to mix up two LEDs.

Anatomy of a line you know

   the class (blueprint)          the constructor call          the instance, given a name
        LED                 ←        LED(17)              ←        led = LED(17)
  • LED alone is the class: a blueprint imported from gpiozero, describing what every LED object has (a pin, an is_lit state) and can do (on, off, blink, ...). It drives nothing by itself.
  • LED(17), class name plus parentheses, is a constructor call: build me one, with these starting facts. Out comes a fresh instance, its pin set to 17, its LED off.
  • led = ... binds a name to that instance, exactly the Week 4 assignment story. The name is a handle; the object is the thing.

Same grammar every time you have used it since: Button(27, bounce_time=0.05) constructs a Button with two starting facts. Constructor arguments are the facts an object cannot invent for itself, and deciding what they should be is a design question you will answer on the next page.

Attributes and methods, precisely

What it isHow it readsExamples you have used
AttributeA fact stored on the instanceNo parentheses; asking costs nothing and changes nothingbutton.is_pressed · led.is_lit · button.when_pressed
MethodAn action the instance can performParentheses, possibly with arguments; doing may change stateled.on() · led.blink(0.2, 0.2) · button.wait_for_press()

The parentheses rule matters more than it looks. led.on without them does not turn anything on; it hands you the method itself, a thing Python is happy to pass around. Week 4's event code depended on exactly that: button.when_pressed = flash stored a function as an attribute's value, to be called later. Objects treat behaviour as just another thing they can hold, and that idea returns on the State & devices page when your own methods become callbacks.

Instances are independent

One blueprint, many things, each with its own state:

One class LED at the top, two constructor arrows down to two separate instances: led_a on pin 17 with is_lit True, and led_b on pin 27 with is_lit False. The instances share the blueprint and nothing else. class LED the blueprint: attributes + methods it defines LED(17) LED(27) instance led_a pin: 17 is_lit: True its own state instance led_b pin: 27 is_lit: False its own state
Shared blueprint, separate state. led_a.on() changes led_a and nothing else. This is the property that makes "two players", "four sensors" or "a dozen relays" a loop instead of a rewrite.

Prove it at the bench in four REPL lines (both LEDs wired as in Week 4's toggle exercise, or mentally if only one is on the board):

>>> from gpiozero import LED
>>> led_a, led_b = LED(17), LED(27)
>>> led_a.on()
>>> led_a.is_lit, led_b.is_lit
(True, False)

Exploring objects in the REPL

Three built-in tools open any object, gpiozero's, Python's, or (from the next page) yours:

ToolQuestion it answersTry it now
type(led)What class made this?<class 'gpiozero.output_devices.LED'>, the blueprint's full address
dir(led)What names does it carry?Every attribute and method, yours to skim; ignore the __dunder__ crowd for now
help(LED)What does the blueprint say?The class's own documentation, constructor arguments included; q to leave

help(LED) opens with a line worth noticing: the class's parentage. In the online docs the same fact appears as "Bases: DigitalOutputDevice": LED is built as a specialization of a more general class, inheriting its features. That is inheritance, and for now you only need to read it: when a method you use on an LED is not listed under LED itself, it lives on a parent. The State & devices page gives you the reading rules.

Everything is an object

The capsule model is not a gpiozero feature; it is how Python is built. Strings, numbers, lists, even functions are objects with methods:

>>> name = "raspberry"
>>> name.upper()
'RASPBERRY'
>>> name.count("r")
3
>>> type(name)
<class 'str'>

You have been calling string methods since your first f-string experiments; str is a class like any other, just one Python ships with. The consequence for today: writing a class is not learning an exotic feature, it is joining the system everything you type already lives in. The tutorial's Classes chapter makes the same point formally, and is this week's core reading.

Checklist for this stage

Check yourself

In button = Button(27, bounce_time=0.05), name the class, the instance, and the constructor arguments.
Class: Button, the blueprint. Instance: the object created by the call, bound to the name button. Constructor arguments: 27 and bounce_time=0.05, the starting facts this object cannot invent for itself.
Why does print(led.on) print something like <bound method ...> instead of turning the LED on?
Without parentheses, led.on is the method itself, an object Python hands to you rather than runs. Parentheses are the "do it" operator. The same fact is why when_pressed = flash (no parentheses) works as a callback assignment.
After led_a = LED(17) and led_b = LED(27), what does led_a.on() do to led_b.is_lit?
Nothing. Instances carry their own state; a method call through one name touches that instance only. They share the blueprint, not the data.
How did led.on() know which GPIO pin to drive, with no pin in the call?
The pin is state stored inside the object at construction (LED(17)). Methods read their own instance's attributes, so the fact travels with the object instead of being repeated at every call.
What does a "Bases:" line in gpiozero's docs tell you?
The class's parent: it was built as a specialization of that more general class and inherits its attributes and methods. If a feature you use is not listed on the class itself, look on the parent.
Is "hello" an object? Defend your answer with a one-line experiment.
Yes: "hello".upper() calls a method on it, and type("hello") names its class, str. Everything in Python, strings, numbers, lists, functions, gpiozero devices, follows the same capsule model.