Python OOP Tutorial 3: classmethods and staticmethods
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Use `@classmethod` when a method needs access to shared class-level state via `cls` (e.g., updating `raise_amount` for all instances).
Briefing
Class methods and static methods solve two different problems in Python class design: class methods let code operate on shared class-level state (and can act like alternative constructors), while static methods bundle related utility logic without needing an instance or the class.
A regular instance method automatically receives the instance as its first argument (conventionally named `self`). To switch that behavior so the method receives the class instead, the tutorial adds the `@classmethod` decorator. The new method, `set_raise_amount`, takes `cls` as the first parameter and an `amount` as the second. Inside the method, it updates a class variable—`cls.raise_amount`—so the change applies across all instances. The example starts with `raise_amount` set to 4%, prints the class value and the values on two `Employee` instances, then calls `employee.set_raise_amount(5%)`. After the call, both instances reflect the new 5% because they reference the same shared class variable. Even calling the class method from an instance still updates the class variable, though that calling style is discouraged because it obscures what’s actually being modified.
The tutorial then connects class methods to a common design pattern: alternative constructors. Instead of forcing users to manually parse inputs before creating objects, a class method can accept a different input format and return a fully constructed instance. Using a realistic scenario, it shows employees represented as hyphen-separated strings containing first name, last name, and salary. Without help, a user would have to split the string, extract fields, and pass them into the normal constructor (which triggers `__init__`). The `from_string` class method removes that burden: it accepts `employee_string`, splits it on `-`, creates a new `Employee` by calling the constructor with the parsed values, and returns the new object. The result is the same as manual parsing, but the class provides a cleaner, safer entry point.
For a real-world parallel, the tutorial points to Python’s `datetime` module, where multiple constructors exist. The default constructor takes year, month, and day, while class methods like `fromtimestamp` accept a timestamp and build the corresponding `datetime` object by parsing and returning a new instance.
Static methods address a different need. They don’t receive `self` or `cls` automatically, so they behave like plain functions placed inside a class for organizational clarity. The tutorial defines `is_workday` as a `@staticmethod` that takes a `day` argument and returns whether it falls on a weekday. It uses Python’s `weekday()` convention (Monday = 0 through Sunday = 6) to return `False` for 5 (Saturday) and 6 (Sunday), and `True` otherwise. A practical rule of thumb is offered: if a method doesn’t access instance state (`self`) or class state (`cls`), it likely belongs as a static method.
By the end, the distinctions are clear: class methods operate on the class and can create objects from alternate inputs; static methods provide class-related utilities without depending on either instance or class state.
Cornell Notes
Python’s method types differ by what gets passed in automatically. Regular instance methods receive `self` (the instance) first. Class methods use `@classmethod` and receive `cls` (the class) first, making them ideal for updating class variables like `raise_amount` across all instances and for alternative constructors such as `from_string`, which parses a hyphen-separated string and returns a new `Employee`. Static methods use `@staticmethod` and receive neither `self` nor `cls`, so they act like regular functions grouped inside a class—useful for logic like `is_workday` that depends only on explicit arguments. These choices clarify when shared state or object creation logic belongs on the class versus when utility logic should stand alone.
How does a class method change the first argument a method receives compared with a regular instance method?
Why do two different `Employee` instances both reflect the updated `raise_amount` after calling `employee.set_raise_amount(5%)`?
What makes a class method an “alternative constructor,” and how does `from_string` demonstrate it?
What is the key behavioral difference between class methods and static methods in terms of automatic arguments?
How does the `is_workday` static method decide whether a given date is a workday?
Review Questions
- When should a method be implemented as a class method instead of a regular instance method? Give an example based on shared class variables.
- Why does calling a class method from an instance still update the class variable, and why is that calling style discouraged?
- What input-parsing problem does `from_string` solve, and what does it return?
Key Points
- 1
Use `@classmethod` when a method needs access to shared class-level state via `cls` (e.g., updating `raise_amount` for all instances).
- 2
Class methods can be called from instances, but the clearer intent is to call them on the class to signal that class state is being modified.
- 3
Treat class methods as alternative constructors when callers need to create objects from different input formats (like a hyphen-separated string).
- 4
Use `@staticmethod` for logic that belongs with a class conceptually but doesn’t require `self` or `cls`; it should operate only on explicit arguments.
- 5
A practical rule: if a method never reads instance variables (`self`) or class variables (`cls`), it likely should be static.
- 6
When designing constructors, returning a newly created object from an alternative constructor keeps object creation consistent and encapsulated.