What are Static and Class Methods?
Static methods and class methods belong to the class itself rather than to any individual object instance. They don’t receive
self(the object) as a first argument. Class methods receivecls(the class), while static methods receive nothing extra — they are pure utility functions scoped inside a class.
Explanation
The Three Method Types Compared
| Method Type | Decorator | First Param | Access | When to Use |
|---|---|---|---|---|
| Instance Method | (none) | self | Instance + class data | Operate on object state (most common) |
| Class Method | @classmethod | cls | Class data only (no instance) | Factories, alternate constructors, class-level logic |
| Static Method | @staticmethod | (none) | Nothing automatic | Utility functions related to the class but needing no context |
class MyClass:
class_var = "shared"
def instance_method(self): # has 'self' → access to object + class
return f"instance: {self.class_var}"
@classmethod
def class_method(cls): # has 'cls' → access to class only
return f"class: {cls.class_var}"
@staticmethod
def static_method(): # no self/cls → pure utility
return "static: no context"
obj = MyClass()
print(obj.instance_method()) # instance: shared
print(MyClass.class_method()) # class: shared
print(MyClass.static_method()) # static: no context
print(obj.static_method()) # also valid via instanceReal-World Analogy
- Think of a library 📚:
- Instance method = A member borrowing a specific book (needs to know which member and which book).
- Class method = The library administrator changing the library’s opening hours (acts on the library as a whole, not a specific member).
- Static method = A utility posted on the wall: “How to find the Dewey Decimal section” (useful to the library, but needs no knowledge of any specific member or current state).
Class Methods as Factory Constructors
- The most powerful use of
@classmethodis creating alternative constructors (factory methods) with meaningful names:
class Date:
def __init__(self, year: int, month: int, day: int):
self.year = year
self.month = month
self.day = day
# ── Factory class methods ─────────────────────────
@classmethod
def from_string(cls, date_str: str) -> "Date":
"""Create Date from 'YYYY-MM-DD' string."""
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day)
@classmethod
def today(cls) -> "Date":
"""Create Date for current date."""
from datetime import date
d = date.today()
return cls(d.year, d.month, d.day)
def __repr__(self) -> str:
return f"Date({self.year}-{self.month:02d}-{self.day:02d})"
d1 = Date(2024, 5, 26) # standard constructor
d2 = Date.from_string("2024-05-26") # factory — from string
d3 = Date.today() # factory — today's date
print(d1) # Date(2024-05-26)
print(d2) # Date(2024-05-26)Implementation
-
A
MathUtilsclass with static utility methods, and aUserclass with class-level factory constructors. Languages: Python · Cpp · Java · Java Script · CSharp
# ─── Python ──────────────────────────────────────────────────────────
import math
class MathUtils:
PI = math.pi # class attribute
# Static methods — no access to self or cls
@staticmethod
def clamp(value: float, min_val: float, max_val: float) -> float:
return max(min_val, min(value, max_val))
@staticmethod
def lerp(a: float, b: float, t: float) -> float:
"""Linear interpolation between a and b at position t (0–1)."""
return a + (b - a) * MathUtils.clamp(t, 0, 1)
@staticmethod
def is_prime(n: int) -> bool:
if n < 2: return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0: return False
return True
# Class method — can access/modify class attributes
@classmethod
def circle_area(cls, radius: float) -> float:
return cls.PI * radius ** 2
class User:
_count = 0
def __init__(self, name: str, email: str, role: str = "user"):
self.name = name
self.email = email
self.role = role
User._count += 1
# Factory class methods
@classmethod
def create_admin(cls, name: str, email: str) -> "User":
return cls(name, email, role="admin")
@classmethod
def from_dict(cls, data: dict) -> "User":
return cls(data["name"], data["email"], data.get("role", "user"))
@classmethod
def get_count(cls) -> int:
return cls._count
def __repr__(self) -> str:
return f"User({self.name!r}, role={self.role!r})"
# Static method usage
print(MathUtils.lerp(0, 100, 0.25)) # 25.0
print(MathUtils.is_prime(17)) # True
print(MathUtils.circle_area(5)) # 78.53...
# Class method / factory usage
u1 = User("Alice", "alice@example.com")
u2 = User.create_admin("Bob", "bob@example.com")
u3 = User.from_dict({"name": "Carol", "email": "carol@ex.com", "role": "mod"})
print(User.get_count()) # 3
print(u2) # User('Bob', role='admin')// ─── C++ ─────────────────────────────────────────────────────────────
#include <iostream>
#include <string>
#include <cmath>
class MathUtils {
public:
static constexpr double PI = 3.14159265358979;
static double clamp(double val, double lo, double hi) {
return std::max(lo, std::min(val, hi));
}
static double lerp(double a, double b, double t) {
return a + (b - a) * clamp(t, 0.0, 1.0);
}
static bool isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i <= (int)std::sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
static double circleArea(double radius) {
return PI * radius * radius;
}
};
class User {
private:
static int count_;
public:
std::string name, email, role;
User(std::string name, std::string email, std::string role = "user")
: name(name), email(email), role(role) { ++count_; }
static User createAdmin(const std::string& name, const std::string& email) {
return User(name, email, "admin");
}
static int getCount() { return count_; }
};
int User::count_ = 0;
int main() {
std::cout << MathUtils::lerp(0, 100, 0.25) << "\n"; // 25
std::cout << MathUtils::isPrime(17) << "\n"; // 1
User u1("Alice", "alice@ex.com");
User u2 = User::createAdmin("Bob", "bob@ex.com");
std::cout << User::getCount() << "\n"; // 2
std::cout << u2.name << ", " << u2.role << "\n"; // Bob, admin
}// ─── Java ─────────────────────────────────────────────────────────────
public class StaticDemo {
static class MathUtils {
public static final double PI = Math.PI;
public static double clamp(double val, double lo, double hi) {
return Math.max(lo, Math.min(val, hi));
}
public static double lerp(double a, double b, double t) {
return a + (b - a) * clamp(t, 0, 1);
}
public static boolean isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i <= Math.sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
}
static class User {
private static int count = 0;
public final String name, email, role;
public User(String name, String email, String role) {
this.name = name; this.email = email; this.role = role;
count++;
}
public static User createAdmin(String name, String email) {
return new User(name, email, "admin");
}
public static int getCount() { return count; }
public String toString() { return "User(" + name + ", " + role + ")"; }
}
public static void main(String[] args) {
System.out.println(MathUtils.lerp(0, 100, 0.25)); // 25.0
System.out.println(MathUtils.isPrime(17)); // true
User u1 = new User("Alice", "alice@ex.com", "user");
User u2 = User.createAdmin("Bob", "bob@ex.com");
System.out.println(User.getCount()); // 2
System.out.println(u2); // User(Bob, admin)
}
}// ─── JavaScript ───────────────────────────────────────────────────────
class MathUtils {
static PI = Math.PI;
static clamp(val, lo, hi) { return Math.max(lo, Math.min(val, hi)); }
static lerp(a, b, t) { return a + (b - a) * MathUtils.clamp(t, 0, 1); }
static isPrime(n) {
if (n < 2) return false;
for (let i = 2; i <= Math.sqrt(n); i++)
if (n % i === 0) return false;
return true;
}
static circleArea(r) { return MathUtils.PI * r * r; }
}
class User {
static #count = 0;
constructor(name, email, role = "user") {
this.name = name; this.email = email; this.role = role;
User.#count++;
}
static createAdmin(name, email) { return new User(name, email, "admin"); }
static fromDict(data) { return new User(data.name, data.email, data.role); }
static getCount() { return User.#count; }
toString() { return `User(${this.name}, ${this.role})`; }
}
console.log(MathUtils.lerp(0, 100, 0.25)); // 25
console.log(MathUtils.isPrime(17)); // true
const u1 = new User("Alice", "alice@ex.com");
const u2 = User.createAdmin("Bob", "bob@ex.com");
console.log(User.getCount()); // 2
console.log(u2.toString()); // User(Bob, admin)// ─── C# ──────────────────────────────────────────────────────────────
using System;
public static class MathUtils {
public const double PI = Math.PI;
public static double Clamp(double val, double lo, double hi) =>
Math.Max(lo, Math.Min(val, hi));
public static double Lerp(double a, double b, double t) =>
a + (b - a) * Clamp(t, 0, 1);
public static bool IsPrime(int n) {
if (n < 2) return false;
for (int i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
}
public class User {
private static int count = 0;
public string Name { get; }
public string Email { get; }
public string Role { get; }
public User(string name, string email, string role = "user") {
Name = name; Email = email; Role = role; count++;
}
public static User CreateAdmin(string name, string email) =>
new User(name, email, "admin");
public static int GetCount() => count;
public override string ToString() => $"User({Name}, {Role})";
static void Main() {
Console.WriteLine(MathUtils.Lerp(0, 100, 0.25)); // 25
Console.WriteLine(MathUtils.IsPrime(17)); // True
var u1 = new User("Alice", "alice@ex.com");
var u2 = User.CreateAdmin("Bob", "bob@ex.com");
Console.WriteLine(User.GetCount()); // 2
Console.WriteLine(u2); // User(Bob, admin)
}
}
Key Takeaways
- Instance methods → need object state (
self). The most common type. - Class methods (
@classmethod) → receivecls, operate on class-level data. Perfect for factory constructors and class-level counters. - Static methods (
@staticmethod) → noselforcls. Pure utility functions scoped inside a class for logical grouping. - In Java/C++/C#,
statickeyword on a method or field means it belongs to the class, shared by all instances. - Static fields/methods are accessed via
ClassName.method()— not via an object instance.