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:
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)
LEDalone is the class: a blueprint imported from gpiozero, describing what every LED object has (a pin, anis_litstate) 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, itspinset 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 is | How it reads | Examples you have used | |
|---|---|---|---|
| Attribute | A fact stored on the instance | No parentheses; asking costs nothing and changes nothing | button.is_pressed · led.is_lit · button.when_pressed |
| Method | An action the instance can perform | Parentheses, possibly with arguments; doing may change state | led.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:
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:
| Tool | Question it answers | Try 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.
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?
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?
How did led.on() know which GPIO pin to drive, with no pin in the call?
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?
Is "hello" an object? Defend your answer with a one-line experiment.
"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.