Comparison of Python Method Types

Feature Instance Method Class Method (@classmethod) Static Method (@staticmethod)
Decorator None @classmethod @staticmethod
First Argument self (the instance object) cls (the class itself) None (no implicit first argument)
Access Instance Data Yes (via self.attribute) No (not directly; must create an instance via cls()) No (unless an instance is passed as an argument)
Access Class Data Yes (via self.__class__.attribute or ClassName.attribute) Yes (via cls.attribute) Yes (via ClassName.attribute)
How It's Called On an instance: obj.method() On class: ClassName.method()
On instance: obj.method() (but cls is still the class)
On class: ClassName.method()
On instance: obj.method()
Primary Purpose Operate on instance-specific data/state. Factory methods, operate on class-level data/state. Utility functions logically grouped with the class.

Detailed Explanations and Examples

1. Instance Method

These are the most common type of methods. They operate on an instance of the class and have access to the instance's attributes and methods via the self argument.

Example Code:

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

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

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

    # Instance method that can access class attributes
    def get_species(self):
        return f"{self.name} belongs to the species: {self.__class__.species}" # or Dog.species

# Usage
my_dog = Dog("Buddy", 3)
print(my_dog.describe())        # Output: Buddy is 3 years old.
print(my_dog.get_species())     # Output: Buddy belongs to the species: Canis familiaris

Benefits:

Use Cases:


2. Class Method (@classmethod)

Class methods are bound to the class and not the instance of the class. They receive the class itself as the first argument, conventionally named cls. They can modify class state that applies across all instances of the class.

Example Code:

class Car:
    num_cars_created = 0  # Class attribute

    def __init__(self, color, model):
        self.color = color
        self.model = model
        Car.num_cars_created += 1

    @classmethod
    def get_num_cars(cls):
        # cls refers to the Car class
        return f"Number of cars created: {cls.num_cars_created}"

    @classmethod
    def create_blue_sedan(cls, model_name):
        # cls() is equivalent to Car() here.
        # This is a factory method.
        print(f"Factory: Creating a blue sedan of type {cls.__name__} with model {model_name}")
        return cls(color="blue", model=model_name)

class ElectricCar(Car):
    # This subclass will correctly use ElectricCar as cls in inherited classmethods
    pass

# Usage
car1 = Car("red", "SUV")
car2 = Car("black", "Hatchback")

print(Car.get_num_cars())         # Output: Number of cars created: 2
print(car1.get_num_cars())        # Output: Number of cars created: 2 (can be called on instance too)

blue_swift = Car.create_blue_sedan("Swift")
print(f"Created: {blue_swift.color} {blue_swift.model}") # Output: Created: blue Swift

# Demonstrating inheritance with class methods
electric_tesla = ElectricCar.create_blue_sedan("Model S") # cls will be ElectricCar
print(f"Created Electric: {electric_tesla.color} {electric_tesla.model}")
print(Car.get_num_cars()) # Will be 4 now (car1, car2, blue_swift, electric_tesla)

Benefits:

Use Cases:


3. Static Method (@staticmethod)

Static methods do not receive an implicit first argument (self or cls). They are essentially regular functions that live inside a class's namespace, primarily because they logically belong to the class, but they don't operate on instance or class state.

Example Code:

class MathUtils:
    PI = 3.14159 # Class attribute, can be accessed via MathUtils.PI

    @staticmethod
    def add(x, y):
        # This method doesn't use self or cls
        return x + y

    @staticmethod
    def is_even(num):
        return num % 2 == 0

    @staticmethod
    def circle_area(radius):
        # Can access class attributes via class name if needed
        return MathUtils.PI * radius * radius

# Usage
sum_val = MathUtils.add(5, 3)
print(f"Sum: {sum_val}")  # Output: Sum: 8

print(f"Is 4 even? {MathUtils.is_even(4)}")  # Output: Is 4 even? True
print(f"Area of circle with radius 2: {MathUtils.circle_area(2)}") # Output: Area of circle with radius 2: 12.56636

# Can also be called on an instance, but it's not common practice as it's misleading
# utils_instance = MathUtils()
# print(utils_instance.add(10, 2)) # Works, but add() doesn't use the instance

Benefits:

Use Cases: