Classes defined the blueprint. An object is what you actually create and use at runtime—the live dataset in memory, the model holding trained weights, the optimizer tracking momentum buffers.
ML pipelines are chains of objects: a DataLoader yields batch objects (tensors), a model object transforms them, a loss object scores predictions. Understanding object state, identity, and lifetime prevents subtle bugs in training scripts.
Learning Objectives
By the end of this lesson, students should be able to:
- Instantiate objects from a class and access their attributes and methods.
- Explain object identity (
is) versus equality (==) in Python. - Describe how object state changes during ML training (weights update, metrics accumulate).
- Trace how objects are passed by reference and why mutating shared state causes bugs.
- Wire together dataset, model, and optimizer objects in a minimal training step.
- Recognize when to create a new object versus modify an existing one.
Creating and Using Objects
Instantiation calls the class like a function. Python allocates memory, runs __init__, and returns a new object:
config = ModelConfig(learning_rate=0.001, batch_size=64, epochs=20)
print(config.summary())
# lr=0.001, batch=64, epochs=20
The variable config holds a reference to the object—not a copy of the class definition. You interact with the object through dot notation: config.learning_rate, config.summary().
An object’s state is the collective value of its instance attributes at a given moment. A neural network’s state is its weight tensors; a metrics tracker’s state is its running loss and accuracy counts. Training changes object state over time.
Objects in an ML Training Step
Consider a simplified training step with three cooperating objects:
import torch
import torch.nn as nn
# Three objects, each with its own state
model = nn.Linear(4, 1) # weights, bias
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
criterion = nn.MSELoss()
# One batch: tensors are objects too
inputs = torch.randn(8, 4)
targets = torch.randn(8, 1)
# State flows through method calls
predictions = model(inputs) # model.forward()
loss = criterion(predictions, targets)
optimizer.zero_grad()
loss.backward() # gradients stored on parameter objects
optimizer.step() # mutates model weight state
Each object has a role. None of them is a loose global variable—they are composed in a pipeline.
Identity vs. Equality
| Operator | Question It Answers | ML Example |
|---|---|---|
is |
Are these the same object in memory? | optimizer.param_groups[0]['params'][0] is model.weight — True (same tensor object) |
== |
Do these objects have equal values? | Two models with identical weights may be == but not is the same object |
a = TabularDataset([[1]], [0])
b = a # b references the same object as a
c = TabularDataset([[1]], [0]) # new object, equal data
print(a is b) # True — same object
print(a is c) # False — different objects
print(a[0] == c[0]) # True — same values
Mutability and Shared State
Most ML objects are mutable—their state can change after creation. This is powerful (training updates weights) but dangerous when objects are unintentionally shared.
history = []
class MetricTracker:
def __init__(self, name, log=history): # DANGER: shared default
self.name = name
self.log = log
train_metrics = MetricTracker("train")
val_metrics = MetricTracker("val")
train_metrics.log.append(0.9)
print(val_metrics.log) # [0.9] — val polluted by train!
Fix: Use None as default and create a fresh list inside __init__. Each object should own its state.
The same principle applies to datasets: if two dataset objects share a mutable list of features and one applies in-place augmentation, both see the change. Clone or copy when isolation is required.
Object Lifetime in a Pipeline
Creation
dataset = ImageDataset(...) — Load paths, set transforms. Heavy I/O may be lazy.
Active Use
Training loop reads and mutates objects: batches drawn, weights updated, logs appended.
Persistence
torch.save(model.state_dict(), ...) serializes state. New model object can reload weights.
# Save and restore model state (new object, same weights)
torch.save(model.state_dict(), "checkpoint.pt")
model_v2 = nn.Linear(4, 1) # fresh object
model_v2.load_state_dict(torch.load("checkpoint.pt"))
Representing Objects as Strings
Implement __repr__ for debugging—essential when logging dozens of experiment objects:
class TabularDataset:
# ... previous methods ...
def __repr__(self):
return f"TabularDataset(n={len(self)}, features={self.num_features()})"
print(train_ds) # TabularDataset(n=1000, features=12)
Common Misconceptions
b = a copies the object.”
Reality: It copies the reference. Both names point to the same object. Use copy.copy() or .clone() for tensors when you need independence.
Reality: Python passes object references. If a function mutates list.append or tensor.add_(), the caller sees the change.
Quick Knowledge Check
- Short Answer: What changes when
optimizer.step()runs? Answer: Model parameter (weight/bias) values—the model object’s state. - True/False:
a is bimpliesa == bfor all objects. Answer: False in general (e.g., NaN), but True for identical references with consistent equality. - Short Answer: Why implement
__repr__on custom dataset classes? Answer: Clear debugging output when printing or logging objects. - Multiple Choice: Two models with identical weights after training are always the same object. (a) True (b) False. Answer: (b) False.
- Short Answer: What is an object’s state? Answer: The collective value of its instance attributes at a given moment.
- True/False:
b = acopies the object so later mutations tobleaveaunchanged. Answer: False—it copies the reference; both names point to the same object. - Short Answer: How should you get an independent tensor copy when needed? Answer: Use
copy.copy()or tensor.clone(). - Multiple Choice: Checkpoints typically: (a) preserve object identity forever, (b) save and restore state without keeping the same identity, (c) delete the class, (d) freeze Python itself. Answer: (b).
- True/False: Python functions receive copies of objects, so
list.appendinside a function cannot affect the caller. Answer: False—Python passes references; mutations are visible to the caller. - Short Answer: Name three cooperating objects in a minimal training step. Answer: Model, optimizer, and loss/criterion (plus tensor batches).
Key Takeaways
- Objects are live instances with mutable state (attributes) and behavior (methods).
- ML pipelines compose objects: datasets, models, losses, optimizers, tensors.
ischecks identity;==checks value equality—different questions.- Shared mutable state causes cross-contamination; each object should own its data when possible.
- Checkpoints save and restore object state without preserving object identity.
Hands-on: Assign b = a on a shared list or tensor, mutate b, and print a. Then repeat with .clone(). Students should feel identity versus copy before talking about checkpoints.
Discussion: After loading a checkpoint, is the restored model the same object as before training? Why does that distinction matter for logging and serving?
nn.Module, custom datasets, and estimator hierarchies.