Python Classes and Methods
A custom Dataset class in PyTorch is built by subclassing the Dataset parent class and implementing __init__, __getitem__, and __len__.
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…
When the Dataset Returns the Wrong Thing
Suppose you build a dataset object, ask it for an item, and get back something you did not expect. Perhaps the wrong feature is paired with the wrong label, or the length does not match your data. Where should you look? This article approaches custom dataset classes from that debugging question. Rather than starting with a list of rules, it starts with behavior you can observe when a custom dataset runs, and then works backward to the code that produces it.
A custom dataset class in PyTorch begins by subclassing PyTorch's Dataset parent class. The subclass inherits from Dataset, and then supplies the behavior that makes the data specific to your problem. Every custom dataset you write is built from three methods: __init__, __getitem__, and __len__. Each one owns a different responsibility, and a wrong result almost always points to one of them rather than to the class as a whole.
What do you think happens?
Imagine two lists, one holding features and one holding labels, aligned so that position 2 in each holds the features and label for the same example. If you ask the dataset for the item at index 2, what should come back, and what should the dataset's length report? Write down your forecast before reading on.
Reveal answer
The item at index 2 should be the pair made of the features and the label stored at position 2, and the length should equal the number of aligned entries in the lists.
Keep this forecast. The point of tracing the class is to compare it against what the code actually does, and to notice the exact moment your mental model and the code disagree. That moment is usually where the bug lives.
Tracing One Request Through the Class
A helpful way to read a custom dataset is to follow a single request from start to finish. The request begins with an index, moves into __getitem__, reads the stored arrays at that position, and returns a pair. Trace this path once and you will know which attributes were touched and which line produced the final value. The source example works with tensors of numerical features. The sketch below uses plain Python lists instead, so the structure stays easy to see. The three methods play the same roles as they would in the tensor version.
4
('loved it', 1)__init__runs once, whenReviewDataset(...)is called, and stores both lists on self__getitem__receives one index and returns one (text, label) pair__len__reports how many labels are storedlen(reviews)calls__len__, and reviews[2] calls__getitem__with index 2
Tracing reviews[2] Line by Line
What does reviews[2] return for the ReviewDataset built in the sketch above?
- 1Call the index operator
Python sees reviews[2] and calls the object's
__getitem__method, passing 2 as the index. The data loader would normally supply this index; here we supply it ourselves. - 2Read the texts array
Inside
__getitem__,self.texts[index] becomesself.texts[2], which is the string 'loved it'. The attributeself.textswas stored earlier by__init__. - 3Read the labels array
self.labels[index] becomesself.labels[2], which is the integer 1. The label sits at the same position as its feature, so the two stay paired. - 4Return the pair
The return statement packages both values into a tuple, giving ('loved it', 1).
('loved it', 1). The features and label at position 2 come back together, which matches the forecast from the start of the article.
Who Owns What Among the Three Methods
The three methods divide the work. One stores the data, one returns a single sample, and one reports the size. Separating them this way means you can ask a precise question about any wrong result: is the stored data wrong, is the sample selected wrongly, or is the count wrong? The diagram below shows how the three methods depend on the same stored attributes.
Following a Call Sequence
The two operations you perform on a dataset, indexing and measuring, trigger different methods in a fixed pattern. Writing reviews[2] runs __getitem__ with index 2, and writing len(reviews) runs __len__. Neither call touches __init__, because __init__ ran earlier, when the object was built. The sequence below follows both calls through the object and its stored arrays.
Mistakes That Make Output Diverge
When a returned sample is wrong, work along the path from __init__ to __getitem__ to the return value. Check the stored attributes first, then the indexing inside __getitem__, then the count reported by __len__. The first step where the output stops matching your forecast is where the divergence starts.
Common mistake Assuming __getitem__ chooses which item to return
What it looks likeCalling reviews[2] and expecting the class to pick a random or convenient sample.
Why it failsThe index is supplied by the caller, and in training the data loader supplies it.
__getitem__returns exactly the item for the index it receives.Do this insteadTreat the index as the input. If the wrong item comes back for index 2, look at how the stored arrays are read at that index, not at how the index was chosen.
Common mistake Keeping data in a local variable inside __init__
What it looks likedef __init__(self, texts, labels): stored = textsWhy it failsA local variable exists only while
__init__runs.__getitem__cannot read it, so it fails when it looks forself.texts.Do this insteadAttach the data to self, as in self.texts = texts, so the other methods can read it later.
Common mistake Measuring the wrong stored array in __len__
What it looks likeReturning len(self.texts) when the labels list was supposed to define the size.
Why it failsThe reported length then describes a different array from the one the samples come from, so the count can disagree with the data you believe you loaded.
Do this insteadMake __len__ measure the array that defines the samples, as the source example does with the row count of the label array.
Common mistake Building the feature list and the label list in different orders, so that position 2 no longer pairs the same example in both.
Practice: Trace a Second Dataset
Using the ReviewDataset class from earlier, predict the output of these two lines before running them: reviews2 = ReviewDataset(['fun ride', 'boring', 'a classic'], [1, 0, 1]), then print(len(reviews2)) and print(reviews2[1]). Write your prediction for both printed lines, then identify which method produced each one.
Hints
len(reviews2)calls__len__, which measures the stored label list.- reviews2[1] calls
__getitem__with index 1, so look at position 1 in both lists. - Remember that the labels list is 1, 0, 1, so position 1 holds 0.
A teammate's dataset returns ('boring', 1) for index 1 when the label list is [1, 0, 1]. Without running any code, name the method where the divergence most likely starts, and explain which stored value you would check first.
Hints
- Ask whether the feature side or the label side is wrong, using the list contents.
- Think about which method reads the stored arrays at a given index.
Key Takeaways
- A custom Dataset class is built by subclassing PyTorch's Dataset parent class and implementing
__init__,__getitem__, and__len__. __init__runs once to store the data on self, so the other methods can read it later.__getitem__returns exactly one item for the index it receives, and the data loader supplies that index.__len__reports the number of items, reading the stored label array in the source example.- To locate a wrong output, follow the path from the stored attributes to the index lookup to the return value, and stop at the first step where the result differs from your forecast.