Skip to content

How to Call a Method on a Class Without Instantiating It in Python

Learn how to use @staticmethod, @classmethod, and class attributes in Python to call methods directly on a class without creating an instance, with practical examples for utility functions, factory methods, and configuration access.

In Python, you typically create an instance of a class before calling its methods. However, there are many scenarios where you want to use a method without creating an object like utility functions, factory methods, configuration accessors, or operations that don't depend on instance-specific data.

Python provides several mechanisms for this, each serving a different purpose. This guide covers the main approaches with practical examples and explains when to use each one.

Using @staticmethod

A static method belongs to the class but doesn't receive the class (cls) or instance (self) as its first argument. It behaves like a regular function that happens to live inside a class namespace: it is useful for utility functions related to the class.

class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y

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

    @staticmethod
    def celsius_to_fahrenheit(celsius):
        return (celsius * 9/5) + 32

print(MathUtils.add(10, 5))
print(MathUtils.is_even(7))
print(MathUtils.celsius_to_fahrenheit(100))

Output:

15
False
212.0

When to Use Static Methods

  • The method doesn't access any class or instance attributes.
  • It's a utility function logically related to the class.
  • You want to organize related functions under a class namespace.

Using @classmethod

A class method receives the class itself (cls) as its first argument, allowing it to access and modify class-level attributes. It's commonly used for alternative constructors and factory methods.

class User:
    user_count = 0

    def __init__(self, name, email):
        self.name = name
        self.email = email
        User.user_count += 1

    @classmethod
    def get_user_count(cls):
        return cls.user_count

    @classmethod
    def from_string(cls, user_string):
        """Alternative constructor: create a User from a string."""
        name, email = user_string.split(',')
        return cls(name.strip(), email.strip())

print(f"Users before: {User.get_user_count()}")

user1 = User.from_string("Alice, alice@example.com")
user2 = User.from_string("Bob, bob@example.com")

print(f"Users after: {User.get_user_count()}")
print(f"User 1: {user1.name} ({user1.email})")

Output:

Users before: 0
Users after: 2
User 1: Alice (alice@example.com)

When to Use Class Methods

  • You need to access or modify class-level attributes.
  • You're creating an alternative constructor (factory method).
  • The method should work correctly with inheritance (subclasses).

Class Methods and Inheritance

A key advantage of class methods over static methods is that they work properly with inheritance : cls refers to the subclass, not the parent:

class Animal:
    @classmethod
    def create(cls, name):
        return cls(name)

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

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

dog = Dog.create("Rex")
print(dog.speak())
print(type(dog))

Output:

Rex says Woof!
<class '__main__.Dog'>

Accessing Class Attributes Directly

The simplest case is class attributes (variables defined in the class body) that are accessible directly on the class without any decorator:

class Config:
    DEBUG = False
    DATABASE_URL = "postgresql://localhost/mydb"
    MAX_CONNECTIONS = 10

print(Config.DEBUG)
print(Config.DATABASE_URL)

Config.DEBUG = True
print(Config.DEBUG)

Output:

False
postgresql://localhost/mydb
True

Comparing the Approaches

class Demo:
    class_var = "I'm a class variable"

    def instance_method(self):
        """Requires an instance (self)."""
        return f"Instance method called on {self}"

    @classmethod
    def class_method(cls):
        """Receives the class (cls), not an instance."""
        return f"Class method called on {cls.__name__}"

    @staticmethod
    def static_method():
        """Receives neither class nor instance."""
        return "Static method called"

print(Demo.class_var)

print(Demo.class_method())

print(Demo.static_method())

obj = Demo()
print(obj.instance_method())

Output:

I'm a class variable
Class method called on Demo
Static method called
Instance method called on <__main__.Demo object at 0x7ff7d1593010>

Practical Example: Configuration Manager

A real-world use case combining static and class methods:

class AppConfig:
    _settings = {
        'debug': False,
        'log_level': 'INFO',
        'max_retries': 3
    }

    @classmethod
    def get(cls, key, default=None):
        """Retrieve a configuration value."""
        return cls._settings.get(key, default)

    @classmethod
    def set(cls, key, value):
        """Update a configuration value."""
        cls._settings[key] = value

    @classmethod
    def all(cls):
        """Return all settings."""
        return cls._settings.copy()

    @staticmethod
    def validate_log_level(level):
        """Check if a log level is valid."""
        valid_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}
        return level.upper() in valid_levels

print("Debug:", AppConfig.get('debug'))
print("Valid log level:", AppConfig.validate_log_level('INFO'))

AppConfig.set('debug', True)
print("Updated debug:", AppConfig.get('debug'))
print("All settings:", AppConfig.all())

Output:

Debug: False
Valid log level: True
Updated debug: True
All settings: {'debug': True, 'log_level': 'INFO', 'max_retries': 3}

Practical Example: Validator Class

class Validator:
    @staticmethod
    def is_email(text):
        return '@' in text and '.' in text.split('@')[-1]

    @staticmethod
    def is_positive(number):
        return isinstance(number, (int, float)) and number > 0

    @staticmethod
    def is_non_empty_string(text):
        return isinstance(text, str) and len(text.strip()) > 0

print(Validator.is_email("user@example.com"))
print(Validator.is_positive(-5))
print(Validator.is_non_empty_string("  "))

Output:

True
False
False

Quick Reference

Approach Access self? Access cls? Use Case
@staticmethod ❌ ❌ Utility functions, validation
@classmethod ❌ ✅ Factory methods, class-level data
Class attributes ❌ ❌ Constants, configuration
Instance method ✅ ❌ Per-object behavior (requires instance)

Conclusion

Python provides clean, built-in mechanisms for calling methods on a class without creating an instance.

  • Use @staticmethod for utility functions that don't need access to the class or instance.
  • Use @classmethod when you need to access class-level attributes or create alternative constructors.
  • Access class attributes directly for constants and configuration values.

By choosing the right approach, you keep your code organized, reduce unnecessary object creation, and make your intent clear to other developers.

Navigation