Skip to content

Comprehensive Guide to Class vs Instance Methods in Python

Updated: at 04:12 AM

Class methods and instance methods are two important concepts in object-oriented programming (OOP) that differ in how they are called and what data they have access to. Understanding the distinction between these two method types is key to writing clean, organized code in Python.

This comprehensive guide will explain class methods and instance methods in detail through examples, best practices, and use cases. By the end, you will have a solid understanding of when and how to use each method type in your own Python programs.

Table of Contents

Open Table of Contents

Introduction

In Python, a class is a blueprint for creating objects. When you create a new object from a class, it is called an instance of that class. Classes allow you to model real-world objects and handle complexity by compartmentalizing code into sensible groupings.

Within a class, you can define methods - functions that are intended to be called on instances of that class. There are two main categories of methods in Python:

Understanding when to use each method type will improve your Python code by keeping responsibilities separated.

We will now explore class methods and instance methods in more detail, with examples demonstrating the key differences and use cases of each.

Instance Methods

Instance methods are the most common method type. They are defined inside a class and intended to be called on instances of that class.

Here is an example Person class with an instance method walk():

class Person:

  def __init__(self, name):
    self.name = name

  def walk(self):
    print(f"{self.name} is walking")

person = Person("John")
person.walk()
# Prints "John is walking"

Note that inside the walk() method, we can access self.name to read and modify the state of that Person instance.

Instance methods can also accept parameters and return values like regular functions:

class Person:

  def __init__(self, name):
    self.name = name

  def walk(self, speed):
    print(f"{self.name} is walking at {speed} mph")

  def run(self):
    return f"{self.name} is running"

person = Person("Mary")

person.walk(3)
# Prints "Mary is walking at 3 mph"

print(person.run())
# Prints "Mary is running"

The key things to notice are:

This encapsulates the state and behaviors within the class, and models real-world objects. Instance methods provide the interface to interacting with each object.

Instance Method Use Cases

Here are some common use cases where instance methods are applicable:

Accessing instance attributes

Instance methods can access and modify attributes on an instance using self:

class BankAccount:

  def __init__(self, balance=0):
    self.balance = balance

  def deposit(self, amount):
    self.balance += amount

Encapsulating instance behavior

Methods can centralize instance behaviors like calculating derived data:

class Circle:

  def __init__(self, radius):
    self.radius = radius

  def area(self):
    return 3.14 * (self.radius ** 2)

Providing instance interface

The public instance methods define how external code interacts with the object:

class Student:

  def __init__(self, name):
    self.name = name
    self.grades = []

  def add_grade(self, grade):
    self.grades.append(grade)

So in summary, instance methods provide interfaces to interact with object instances.

Class Methods

While instance methods operate on instances, class methods are methods that are called on the class itself.

Class methods take a cls parameter instead of self. This refers to the class, allowing you to access class attributes and call other class methods.

Here’s an example class method on our Person class:

class Person:

  num_people = 0

  def __init__(self, name):
    self.name = name
    Person.num_people += 1

  @classmethod
  def population(cls):
    return cls.num_people

print(Person.population()) # 0

p1 = Person("Sarah")
p2 = Person("Mike")

print(Person.population()) # 2

We use the @classmethod decorator to indicate this is a class method, taking cls rather than self.

To call a class method, we use the class name rather than an instance: Person.population() rather than p1.population(). The class method can access Person.num_people because that variable is stored on the class itself.

Class Method Use Cases

Here are some common use cases for class methods:

Alternative constructors

We can use class methods to provide multiple ways of constructing the class:

class Rectangle:

  def __init__(self, width, height):
    self.width = width
    self.height = height

  @classmethod
  def from_area(cls, area):
    width = area ** 0.5
    height = area / width
    return cls(width, height)

rect = Rectangle.from_area(25)
# Constructs a 5x5 rectangle from area

Factory methods

We can use class methods to construct instances of various subclasses:

class Vehicle:

  @classmethod
  def create(cls, type, wheels):
    if type == "car":
      return Car(wheels)
    elif type == "truck":
      return Truck(wheels)

car = Vehicle.create("car", 4)
truck = Vehicle.create("truck", 8)

Stateless utility methods

Class methods can also encapsulate utility functions that don’t require any instance state:

import datetime

class DateUtils:

  @classmethod
  def start_of_month(cls, any_date):
    return datetime.datetime(any_date.year, any_date.month, 1)

first_day = DateUtils.start_of_month(datetime.date(2023, 2, 15))
print(first_day) # 2023-02-01

So in summary, class methods provide interfaces for the class itself and encapsulate stateless utilities.

Comparing Instance and Class Methods

Let’s recap the key differences between instance and class methods:

Here is a table summarizing these differences:

Instance MethodsClass Methods
Call Syntaxinstance.method()Class.method()
First Parameterselfcls
AccessInstance attributesClass attributes & methods
Use CasesInstance behaviorsClass utilities & alternative constructors

And here is an example class with both types of methods:

class ChessPlayer:

  num_players = 0

  def __init__(self, name, elo):
    self.name = name
    self.elo = elo
    ChessPlayer.num_players += 1

  def update_elo(self, d_elo):
    self.elo += d_elo

  @classmethod
  def count_players(cls):
    return cls.num_players

magnus = ChessPlayer("Magnus", 2872)
hikaru = ChessPlayer("Hikaru", 2789)

print(ChessPlayer.count_players()) # 2

magnus.update_elo(10)

print(magnus.elo) # 2882

The update_elo() method encapsulates an instance behavior while count_players() is a class method utility.

Best Practices

Here are some best practices when using class and instance methods in Python:

By following these best practices, your classes will be better organized with clear responsibilities.

Example Use Cases

Let’s explore some end-to-end examples demonstrating good use cases of class methods and instance methods.

Data Transfer Object

A data transfer object (DTO) is an object whose primary purpose is to hold data attributes and values. DTOs can utilize class methods as alternative constructors to create instances:

class UserDTO:

  def __init__(self, username, email):
    self.username = username
    self.email = email

  @classmethod
  def from_dict(cls, data):
    return cls(
      data['username'],
      data['email']
    )

  @classmethod
  def from_api(cls, user_id):
    response = api_client.get_user(user_id)
    return cls.from_dict(response)

# Construct from dict
user1 = UserDTO.from_dict({
  'username': 'jdoe12',
  'email': '[email protected]'
})

# Construct from API
user2 = UserDTO.from_api(api_user_id)

Here the class methods provide flexibility in constructing UserDTO instances from different sources.

Database Models

Database models can leverage both instance and class methods:

class User(db.Model):

  id = db.Column(db.Integer, primary_key=True)
  username = db.Column(db.String)

  def __repr__(self):
    return f"<User {self.username}>"

  @classmethod
  def get_by_id(cls, user_id):
    return cls.query.get(user_id)

  def set_password(self, password):
    self.password_hash = generate_password_hash(password)

# Get user by ID
user = User.get_by_id(123)

# Instance method to set password
user.set_password('secret123')

Here get_by_id() is a class method alternative constructor while set_password() encapsulates instance behavior related to that User object.

Factory Method

The factory method pattern uses class methods to construct various subclasses:

class VehicleFactory:

  @classmethod
  def create_vehicle(cls, type, model):
    if type == "car":
      return Car(model)
    elif type == "truck":
      return Truck(model)
    else:
      raise ValueError(f"Invalid vehicle type: {type}")

car = VehicleFactory.create_vehicle("car", "Prius")
truck = VehicleFactory.create_vehicle("truck", "F-150")

This encapsulates the logic to instantiate diverse subclasses in a single factory class method.

Conclusion

To summarize, instance methods operate on object instances while class methods operate on the class itself.

Knowing when to use each method type leads to better encapsulated and organized code:

Following best practices like preferring instance methods and using descriptive names will improve your class design. And leveraging class and instance methods appropriately in examples like data models, DTOs, and factories showcases their power.

With this comprehensive guide, you now have a deep understanding of when and how to use class methods vs instance methods in your own Python programs. The ability to design proper classes and leverage object-oriented techniques will take your Python skills to the next level.