Defining Your Own Classes
Objects follow a predictable lifecycle: construction (via __init__), operation (via method calls that modify state), and destruction (via __del__).
Try it: Classes, Instances and Attributes
How a class creates instances with __init__ and self, why an attribute read checks the instance's __dict__ before the class, why assigning through an instance never changes the class, how a class-level list is shared by every instance, and why obj.method(x) is Class.method(obj, x).
How it works
- Dog(...) makes a new, empty instance and calls Dog.__init__(new_object, ...); self.name = ... stores into that object's __dict__.
- Reading obj.attr looks in obj.__dict__ first, then in the class.
- Assigning obj.attr = value always writes obj.__dict__, so it shadows a class attribute for that one object only.
- Mutating a class-level list (self.tricks.append) changes the one list every instance shares.
- obj.method is found on the class and bound to obj, so obj.method(x) runs Class.method(obj, x).
Default run (30 steps): Ready: class Dog and the statements you picked. Watch where each attribute is found and where each assignment writes. … Finished. Printed: ['sit'] / Wolf / Canis familiaris / Fido is 5.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Object numbers are illustrative, not real id() values.
Loading the simulation…
From Creation to Cleanup
An object-oriented program becomes easier to understand when you follow an object's lifecycle. The central pattern has three phases: construction, operation, and destruction. During construction, __init__ sets up the object's internal data attributes. During operation, methods are called with dot notation and may change the object's internal state. During destruction, Python calls __del__ to clean up the object.
Construction with __init__
The constructor is named __init__. Python calls it automatically when you create an object. Its role is to set up the object's internal data attributes, giving the object the initial structure it needs before methods operate on it.
class Counter:
def __init__(self):
self.value = 0
counter = Counter()- 1The class definition describes a Counter object.
- 2The
__init__method is the constructor. - 3The assignment inside
__init__sets up the object's internal value attribute. - 4Creating
Counter()automatically invokes__init__.
Methods and State Changes
Methods are functions that belong to objects. They are called with dot notation, such as object.method(). A method can modify the object's internal state, so the same object can contain different values after successive operations.
Tracing Two Method Calls
Determine how the internal value changes in the Counter example.
- 1Create the object
Counter()invokes__init__, which sets value to 0. - 2First increment
counter.increment()invokes the method, which changes value from 0 to 1. - 3Second increment
The same method changes the same object's value from 1 to 2.
After the two method calls, the object's internal value is 2.
The important point is that counter.increment() is not just a label for an action. Python looks up the increment method on counter, calls it, and allows that method to modify the object's internal state.
Bracket Access and __getitem__
Square bracket access is another form of method invocation. For an object named stuff, the expression stuff[0] is shorthand for stuff.__getitem__(0). The source also identifies list.__getitem__(stuff, 0) as an equivalent form. In each form, the object is accessed with the key or index 0.
| Written form | Underlying form |
|---|---|
| stuff[0] | stuff.__getitem__(0) |
| stuff.__getitem__(0) | direct call to the object's __getitem__ method |
| list.__getitem__(stuff, 0) | equivalent list method form |
The three equivalent forms identified in the source
In the example, item_a, item_b, and item_c each refer to the item selected with index 0.Reading Each Line Mechanically
When reading object-oriented code, replace surface syntax with the mechanism it represents. Creating list() invokes construction and automatic initialization. Calling stuff.append('item') looks up the append method on stuff and calls it with the supplied parameter; the method changes the list's internal state. Reading stuff[0] means recognizing the shorthand call to __getitem__(0). When the object is no longer needed at program end, Python calls __del__ for cleanup.
A Complete Trace
Map each operation in a short list example to its underlying mechanism.
- 1stuff = list()
A list object is created, and its constructor initializes the object's internal structure.
- 2stuff.append('item')
Python looks up append on stuff, calls it with item, and the method modifies the list's internal state.
- 3stuff[0]
The bracket expression is shorthand for
stuff.__getitem__(0), which accesses the item at index 0. - 4Program completion
When stuff is no longer needed at the end of the program, Python calls
__del__to perform cleanup.
The visible syntax is a compact way to express construction, method-based state changes, item access, and destruction.
What do you think happens?
What mechanism does stuff[0] represent?
Reveal answer
A shorthand call to stuff.__getitem__(0)
Square bracket syntax is defined as a shorthand for calling the object's __getitem__ method with the supplied key or index.
Mistakes in Lifecycle Tracing
Common mistake Treating __init__ as an ordinary method that must be called manually after every object is created
What it looks likeobj = SomeClass(); obj.__init__()Why it failsThe constructor is called automatically when the object is created.
Do this insteadRead the creation expression as the event that invokes __init__.
Common mistake Assuming a method call only produces an action and does not change the object
What it looks likestuff.append('item')Why it failsMethods can modify the object's internal state.
Do this insteadRecord the object's state before and after each method call.
Common mistake Thinking stuff[0] is unrelated to __getitem__
What it looks likeitem = stuff[0]Why it failsSquare bracket syntax is a shorthand for
stuff.__getitem__(0).Do this insteadTranslate bracket access into the corresponding __getitem__ call while tracing.
Common mistake Stopping the lifecycle trace after the final visible operation
What it looks likeThe trace ends immediately after the last method call.
Why it failsThe lifecycle also includes destruction, during which Python calls
__del__when the object is no longer needed.Do this insteadInclude cleanup as the final phase of the lifecycle.
For each expression below, name the underlying mechanism it invokes: creating an object with list(), changing it with append(), and accessing an item with [0]. Then place those operations in the construction, operation, or destruction phase.
Hints
- Creation invokes the constructor and initialization.
- A method call can modify internal state.
- Bracket access maps to
__getitem__.
Lifecycle Checklist
- 1Object construction automatically invokes
__init__, which sets up internal data attributes. - 2Methods belong to objects, are called with dot notation, and can modify internal state.
- 3Square bracket syntax such as stuff[0] is shorthand for
stuff.__getitem__(0). - 4A complete trace includes construction, operation, and destruction.
- 5When an object is no longer needed at program completion, Python calls
__del__for cleanup.
Key Takeaways
- An object follows a lifecycle of construction, operation, and destruction.
__init__is called automatically during object creation to establish internal data attributes.- Method calls made with dot notation can change the object's internal state.
- stuff[0],
stuff.__getitem__(0), andlist.__getitem__(stuff, 0)are equivalent forms identified in the source. - To understand object-oriented code, map each visible expression to the mechanism it invokes.