Object-Oriented Programming

What professional code looks like when it doesn't live in one long file.

Fundamentals 13 min Beginner April 26, 2026

You have been using objects since your first line of Python. "Hello" is an object of class str, [1, 2, 3] is an object of class list, and every time you called .append() or .upper(), you were calling a method on an object. Object-oriented programming (OOP) is the system behind this: it bundles data and the operations that belong to it into reusable units.

When you open PyTorch, Hugging Face, or scikit-learn, everything you interact with — models, tokenizers, optimizers — is an object created from a class. Understanding OOP turns cryptic library documentation into something you can actually read.

The Class: A Blueprint for Objects

Class

AnalogyDefinition
A class is like a car manufacturer's blueprint for a specific model. The blueprint specifies that every car will have a color, an engine power rating, and a VIN — and that every car can accelerate and brake. But the blueprint itself does not drive — it only describes what the cars built from it will be able to do.

The blueprint analogy breaks at three points: (1) Software classes can inherit from other classes — physical blueprints don't compose this way. (2) Python objects can gain new attributes at runtime — like a car suddenly growing wings. (3) Creating a new software object costs almost nothing, while building a new car costs materials and labor.

How an Object Is Born

1
Code reaches Dog("Bello", "Labrador")
2
Python creates an empty object in memory
3
Python calls __init__(new_object, "Bello", "Labrador")
4
self.name = "Bello" and self.breed = "Labrador" are set
5
Finished object is returned and stored in my_dog

Example: A Dog Class

class Dog:
    def __init__(self, name, breed):
        self.name = name      # Attribute
        self.breed = breed    # Attribute

    def bark(self):            # Method
        print(f"Woof! I am {self.name}")

my_dog = Dog("Bello", "Labrador")
my_dog.bark()  # Output: Woof! I am Bello

class defines the blueprint. __init__ sets up each new object. self always refers to the specific object being created or used. Attributes (self.name) store data on the object. Methods (bark) define behavior.

Common Mistake: Forgetting self

If you forget self in a method signature, you get a confusing error message. Python automatically passes the object as the first argument — without self, the method expects zero arguments but receives one.

class Dog:
    def bark():  # self is missing!
        print("Woof!")

my_dog = Dog()
my_dog.bark()
# TypeError: bark() takes 0 positional
# arguments but 1 was given

The Object: A Living Instance

Object

AnalogyDefinition
If the class is "VW Golf" (the model), the object is the specific red Golf parked in front of your house with 23,456 km on the odometer and a scratch on the left door. Five Golfs from the same factory are five independent objects sharing the same blueprint but having different mileage, damage, and owners.

The car analogy breaks because: (1) Software objects are destroyed when the program ends — real cars persist physically. (2) Objects can query their own class (type(dog1)), while cars don't "know" their blueprint. (3) Objects can be trivially copied — creating a perfect car clone is impossible.

Class (Blueprint)

Defines structure and behavior. Does not act itself. Dog describes what dogs can do — but Dog does not bark.

Object (Instance)

Has its own data and executes methods. my_dog has the name "Bello" and can bark().

Instance Independence

dog1 = Dog("Bello", "Labrador")
dog2 = Dog("Rex", "Shepherd")

print(dog1.name)  # Bello
print(dog2.name)  # Rex

dog1.name = "Wuffi"
print(dog1.name)  # Wuffi
print(dog2.name)  # Rex  (unchanged!)

Each object has its own attribute values. Changing dog1.name has no effect on dog2.name — they are independent copies of the blueprint. This is the core principle: objects share structure, but not data.

Why OOP Is in Every AI Library

The three major AI libraries — PyTorch, Hugging Face Transformers, and scikit-learn — are entirely built on classes and objects. You don't need to write your own classes to use AI — but you need to understand that you are using objects and calling methods on them.

PyTorch

Every model inherits from nn.Module. The class defines layers in __init__ and data flow in forward(). model = nn.Linear(10, 5) creates an object with .weight and .bias attributes.

Hugging Face

GPT2Model.from_pretrained("gpt2") is a class method that creates an object and loads pretrained weights. The object can then be called like a function: model(**inputs).

scikit-learn

clf = DecisionTreeClassifier() creates an object. clf.fit(X, y) trains it — the learned state is stored in the object. clf.predict(X_test) uses that state for predictions.

The pattern is the same everywhere: import makes the class available, the constructor (or a factory method) creates an object, and method calls use or modify the object's state. When you read .fit(), .predict(), or .forward(), you are reading method calls on objects.

Not Everything Needs a Class

Understanding OOP does not mean wrapping everything in classes. Simple scripts work perfectly with functions. Use classes when you need reusable, stateful units — not as an end in themselves.

Attributes defined directly in the class (outside of __init__) are shared by all instances. Attributes set in __init__ via self belong to the individual object.

class Dog:
    species = "Canis lupus familiaris"  # Class attribute

    def __init__(self, name):
        self.name = name  # Instance attribute

dog1 = Dog("Bello")
dog2 = Dog("Rex")
print(dog1.species)  # Canis lupus familiaris
print(dog2.species)  # Canis lupus familiaris (shared!)
print(dog1.name)     # Bello (individual)

Watch out for mutable class attributes! A list as a class attribute is shared by all instances — changes by one object affect all others. Rule of thumb: always define mutable data (lists, dictionaries) in __init__ as instance attributes.

class MyModel(nn.Module) means: MyModel IS an nn.Module with extra features. The child inherits all methods and attributes of the parent and can add its own or override existing ones.

import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()  # Call parent init
        self.layer = nn.Linear(10, 5)

    def forward(self, x):
        return self.layer(x)

This is how every custom PyTorch model works. Inheritance, polymorphism, and method overriding will be explored in depth in later articles.

Interactive: The OOP Landscape

Click on concepts in the concept map to explore their connections. Observe how class and object relate, how methods and attributes are defined and used — and where inheritance comes into play. Pay special attention to how many connections originate from the class node.

Click on a concept to learn more. The lines show how the concepts are connected.
createsdefinesdefineshas values forcallsextendsuses / modifiesClassObjectMethodAttributeInheritance

Click on a circle to see details.

Legend

Class
Object
Method
Attribute
Inheritance

Key Takeaways

  1. A class is a blueprint: it defines which attributes (data) and methods (behavior) every instance will have — but it is not an object itself in the everyday sense.
  2. An object is a living instance of a class. You create it by calling the class name like a function: my_dog = Dog("Bello", "Labrador").
  3. Each object has its own attribute values. Changing dog1.name does not affect dog2.name — they are independent copies of the blueprint.
  4. __init__ sets up a new object's starting state. self refers to the specific object being created or used. Both are conventions, not magic.
  5. AI libraries (PyTorch, Hugging Face, scikit-learn) are built on classes and objects. Learning to read .fit(), .predict(), .forward() as method calls on objects is the key to using them.

Quiz: Object-Oriented Programming

Question 1 / 4
Not completed

What is the difference between a class and an object?

Select one answer
Answer Key: 1) B · 2) C · 3) B · 4) B

Checkpoint: Test Your Knowledge

  • What is the difference between a class and an object? In the Dog example with my_dog = Dog("Bello", "Labrador") — which is the class and which is the object?
  • Can two objects of the same class have different attribute values? Explain using the example where dog1.name = "Wuffi" but dog2.name stays "Rex".
  • When you write model = GPT2Model.from_pretrained("gpt2"): What role do import, the class definition, and the resulting model object each play?