Python Intermediate and Advanced

Python Intermediate_013: Understanding @classmethod in Python

codeaddict 2025. 4. 5. 21:32

In this lesson, we’ll learn about @classmethod . The @classmethod decorator is a neat feature in Python that lets you define methods tied to a class itself, not just its instances.

if you don’t know decorators yet, I have a lesson in Python Intermediate_006: Decorators in Python (https://medium.com/@staytechrich/python-intermediate-006-decorators-in-python-c7c7aaac7c8b) that’ll help you out. Let’s get started with some easy examples!

1. Basic @classmethod Example

Here’s a simple class method that greets you from the class level, not an instance:

class Greeter:
    message = "Hello from the class!"

    @classmethod
    def say_hello(cls):
        print("1. Inside class method: Accessing class data")
        return cls.message

print("START")
result = Greeter.say_hello()
print(f"RESULT: {result}")
print("END")

Output:

START
1. Inside class method: Accessing class data
RESULT: Hello from the class!
END

Order of Execution

Let’s break it down step-by-step:

  1. @classmethod — Marks say_hello as a class method, passing cls (the class) instead of self.
  2. print(“START”) — Prints “START” before the call.
  3. result = Greeter.say_hello() — Calls the class method directly on Greeter.
  4. print(“1. Inside class method…”) — Runs inside say_hello, showing it’s active.
  5. return cls.message — Returns the class variable message (“Hello from the class!”).
  6. print(f”RESULT: {result}”) — Prints the returned value.
  7. print(“END”) — Prints “END”.

This is like a decorator wrapping a function, but here it shifts the focus to the class itself!

2. Practical Example: Instance Tracker

Now, let’s get practical. Imagine a class that tracks how many instances it’s created — perfect for debugging or stats. We’ll use @classmethod to report it.

class InstanceTracker:
    count = 0

    def __init__(self):
        InstanceTracker.count += 1  # Bump count when an instance is made

    @classmethod
    def get_count(cls):
        print("1. Inside class method: Checking instance count")
        return f"Total instances: {cls.count}"

# Test it
print("START")
t1 = InstanceTracker()
t2 = InstanceTracker()
result = InstanceTracker.get_count()
print(f"RESULT: {result}")
print("END")

Output:

START
1. Inside class method: Checking instance count
RESULT: Total instances: 2
END

Order of Execution (for the full test)

  1. print(“START”) — Prints “START” before anything happens.
  2. t1 = InstanceTracker() — Creates the first instance, __init__ runs, count becomes 1.
  3. t2 = InstanceTracker() — Creates the second instance, count becomes 2.
  4. result = InstanceTracker.get_count() — Calls the class method.
  5. print(“1. Inside class method…”) — Runs inside get_count, showing it’s working.
  6. return f”Total instances: {cls.count}” — Returns “Total instances: 2” using cls.count.
  7. print(f”RESULT: {result}”) — Prints the result.
  8. print(“END”) — Prints “END”.

For an instance call (e.g., t1.get_count()), it works the same way — cls still refers to InstanceTracker!

3. Why @classmethod Rocks

  • Class-Level Power: Access shared data (like count) without needing an instance.
  • Factory Vibes: Use it for alternative constructors (more on that below).

Bonus Example: Factory Method

Here’s a quick factory method using @classmethod, like a mini-decorator trick:

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

    @classmethod
    def from_string(cls, data):
        print("1. Inside class method: Parsing string")
        name, age = data.split("-")
        return cls(name, int(age))

print("START")
p = Person.from_string("Alice-30")
print(f"RESULT: {p.name}, {p.age}")
print("END")

Output:

START
1. Inside class method: Parsing string
RESULT: Alice, 30
END

Explanation Step by Step

1. What’s Happening When You Call Person.from_string(“Alice-30”)?

  • Normally, to make a Person object, you’d write something like person1 = Person(“Alice”, 30). This calls the class directly with two arguments (name and age).
  • But here, we’re using Person.from_string(“Alice-30”). This calls the @classmethod from_string instead of making the object right away. It’s a different way to create a Person.

2. Inside from_string(cls, data)

  • The @classmethod decorator changes how this method works. Instead of getting self (which is for instances), it gets cls, which stands for the class itself — in this case, Person.
  • data is the input you give when you call it. Here, data is the string “Alice-30”.
  • The method’s job is to take that string, break it apart, and use it to make a Person object.

3. What data Does in from_string

  • Inside from_string, data is the string “Alice-30” because that’s what we passed when we called Person.from_string(“Alice-30”).
  • The line name, age = data.split(“-”) takes that string and splits it at the — into two parts:
  • “Alice-30” → [“Alice”, “30”]
  • Then, name gets “Alice”, and age gets “30”.
  • int(age) turns the string “30” into the number 30 because age in __init__ expects a number, not a string.
  • Finally, cls(name, int(age)) is like saying Person(“Alice”, 30) — it creates a new Person object using the class (cls) and returns it.

4. How It All Comes Together

  • p = Person.from_string(“Alice-30”) calls the class method.
  • data starts as “Alice-30”, gets split into name = “Alice” and age = “30”, and then makes a Person object with those values.
  • p becomes a Person object with p.name = “Alice” and p.age = 30.

Wrap-Up

Try practicing more with InstanceTracker to log creation times or extend Person with more parsing options. Happy Coding!!