Skip to main content

Python Classes

Introduction

Classes are one of the core features of Python and form the foundation of Object-Oriented Programming (OOP) in Python. Understanding classes is essential for working with PyTorch, as PyTorch's architecture heavily utilizes classes to represent neural networks, datasets, and other components.

In this tutorial, we'll explore what Python classes are, how they work, and why they're important for PyTorch development. Classes allow us to create custom data types that bundle data and functionality together, providing a more organized and reusable code structure.

What is a Class?

A class is a blueprint for creating objects. It defines:

  1. Attributes - data or variables that describe the object
  2. Methods - functions that define the behavior of the object

Think of a class as a template, and objects as concrete instances created from that template.

Creating Your First Class

Here's a simple class definition in Python:

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

# Constructor method
def __init__(self, name, age):
# Instance attributes
self.name = name
self.age = age

# Instance method
def description(self):
return f"{self.name} is {self.age} years old"

# Instance method
def speak(self, sound):
return f"{self.name} says {sound}"

Let's break this down:

  • class Dog: defines a new class named Dog.
  • species = "Canis familiaris" is a class attribute, shared by all instances.
  • __init__ is a special method (constructor) called when a new object is created.
  • self refers to the instance being created.
  • description() and speak() are instance methods that operate on the instance's data.

Creating Objects from a Class

Once you've defined a class, you can create instances (objects) of that class:

python
# Create an instance
buddy = Dog("Buddy", 9)
miles = Dog("Miles", 4)

# Access attributes
print(buddy.name) # Output: Buddy
print(buddy.age) # Output: 9
print(buddy.species) # Output: Canis familiaris

# Call methods
print(buddy.description()) # Output: Buddy is 9 years old
print(buddy.speak("Woof!")) # Output: Buddy says Woof!
print(miles.speak("Bark!")) # Output: Miles says Bark!

Instance vs. Class Attributes

Understanding the difference between instance and class attributes is crucial:

  • Instance attributes are specific to each object (defined inside __init__)
  • Class attributes are shared across all instances of the class
python
class Dog:
# Class attribute
species = "Canis familiaris"
count = 0

def __init__(self, name):
self.name = name # Instance attribute
Dog.count += 1 # Update class attribute

# Create instances
dog1 = Dog("Buddy")
dog2 = Dog("Miles")

print(dog1.name) # Output: Buddy
print(dog2.name) # Output: Miles
print(Dog.count) # Output: 2

Inheritance

Inheritance allows a class to inherit attributes and methods from another class:

python
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age

def show_info(self):
return f"{self.name} is {self.age} years old"

class Cat(Pet):
def speak(self):
return "Meow!"

class Dog(Pet):
def speak(self):
return "Woof!"

# Create instances
fluffy = Cat("Fluffy", 3)
buddy = Dog("Buddy", 5)

print(fluffy.show_info()) # Output: Fluffy is 3 years old
print(fluffy.speak()) # Output: Meow!
print(buddy.speak()) # Output: Woof!

In this example, Cat and Dog inherit the __init__ and show_info methods from Pet.

Method Overriding

You can customize inherited methods by redefining them in the child class:

python
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age

def show_info(self):
return f"Name: {self.name}, Age: {self.age}"

class Dog(Pet):
def __init__(self, name, age, breed):
# Call parent's __init__ method
super().__init__(name, age)
self.breed = breed

# Override the show_info method
def show_info(self):
return f"Name: {self.name}, Age: {self.age}, Breed: {self.breed}"

buddy = Dog("Buddy", 5, "Golden Retriever")
print(buddy.show_info()) # Output: Name: Buddy, Age: 5, Breed: Golden Retriever

Classes in PyTorch Context

PyTorch extensively uses classes for structuring its components. Here's a simple example of how you might define a neural network class using PyTorch:

python
import torch
import torch.nn as nn

class SimpleNetwork(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(SimpleNetwork, self).__init__()
self.layer1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.layer2 = nn.Linear(hidden_size, output_size)

def forward(self, x):
x = self.layer1(x)
x = self.relu(x)
x = self.layer2(x)
return x

# Create an instance of the neural network
model = SimpleNetwork(input_size=784, hidden_size=128, output_size=10)
print(model)

This would output the structure of the neural network:

SimpleNetwork(
(layer1): Linear(in_features=784, out_features=128, bias=True)
(relu): ReLU()
(layer2): Linear(in_features=128, out_features=10, bias=True)
)

Practical Example: Creating a Dataset Class

Here's how you might create a custom dataset class for PyTorch:

python
import torch
from torch.utils.data import Dataset

class NumberDataset(Dataset):
def __init__(self, start=0, end=100):
self.numbers = list(range(start, end))
self.length = len(self.numbers)

def __len__(self):
return self.length

def __getitem__(self, idx):
number = self.numbers[idx]
# Create a feature (x) and target (y) where y = 2*x
x = torch.tensor([number], dtype=torch.float32)
y = torch.tensor([number * 2], dtype=torch.float32)
return x, y

# Create and use the dataset
dataset = NumberDataset(0, 5)
print(f"Dataset length: {len(dataset)}")

for i in range(len(dataset)):
x, y = dataset[i]
print(f"Item {i}: x={x.item()}, y={y.item()}")

Output:

Dataset length: 5
Item 0: x=0.0, y=0.0
Item 1: x=1.0, y=2.0
Item 2: x=2.0, y=4.0
Item 3: x=3.0, y=6.0
Item 4: x=4.0, y=8.0

Additional Features of Classes

Static Methods

Static methods belong to the class but don't operate on an instance:

python
class MathUtils:
@staticmethod
def add(a, b):
return a + b

@staticmethod
def multiply(a, b):
return a * b

# No need to create an instance
print(MathUtils.add(5, 3)) # Output: 8
print(MathUtils.multiply(5, 3)) # Output: 15

Class Methods

Class methods operate on the class itself, not on instances:

python
class Dog:
count = 0

def __init__(self, name):
self.name = name
Dog.count += 1

@classmethod
def get_count(cls):
return f"There are {cls.count} dogs"

dog1 = Dog("Buddy")
dog2 = Dog("Miles")
print(Dog.get_count()) # Output: There are 2 dogs

Properties

Properties allow controlled access to attributes:

python
class Person:
def __init__(self, name, age):
self._name = name
self._age = age

@property
def age(self):
return self._age

@age.setter
def age(self, value):
if value < 0:
raise ValueError("Age cannot be negative")
self._age = value

person = Person("Alice", 30)
print(person.age) # Output: 30

# Using the setter
person.age = 31
print(person.age) # Output: 31

# This would raise an error
# person.age = -5

Summary

Python classes are a powerful way to organize code by bundling data and functionality together. Key concepts we covered:

  • Creating classes with class keyword
  • Using constructors with __init__
  • Differentiating between class and instance attributes
  • Implementing inheritance for code reuse
  • Method overriding for customization
  • Using classes in PyTorch context

Understanding classes is essential for working effectively with PyTorch, as its architecture is built around object-oriented principles. Classes help create reusable, organized code, which is particularly important in complex deep learning applications.

Exercises

  1. Create a Rectangle class with width and height attributes and methods to calculate area and perimeter.
  2. Extend the Rectangle class to create a Square class that ensures width and height are equal.
  3. Create a custom PyTorch dataset class that loads and preprocesses images from a directory.
  4. Implement a simple neural network class using PyTorch's nn.Module to classify MNIST handwritten digits.

Further Reading



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)