skip to content

Why is @classmethod the idiom for alternative constructors in Python?

level: middleimportance: should knowfreq 58%

answer

  1. Why not just call the class directly
  2. What happens when a subclass calls it
  3. cls is bound at call time
  4. from_rows, fromkeys, fromtimestamp
  5. A hardcoded class name breaks inheritance

basics

~20 s

Because a classmethod receives the actual class as cls, so returning cls(...) builds the right type even when a subclass calls it. A factory that names the class directly hardcodes it and silently breaks inheritance.

solid answer

~40 s

`__init__` should take one canonical set of arguments; every other way of building the object becomes a named classmethod that parses its input and delegates to `cls(...)`. The decisive property is that `cls` is bound at call time to the class the attribute was looked up on, so `FlagSet.from_rows(...)` returns a `FlagSet` while `AuditedFlagSet.from_rows(...)` returns an `AuditedFlagSet` with nothing overridden. The standard library uses the idiom everywhere: `dict.fromkeys`, `datetime.date.today`, `datetime.datetime.fromtimestamp`. The alternatives are worse — overloading `__init__` with mutually exclusive parameters and a pile of branches, or a `@staticmethod` factory that names the class in its body and quietly hands back the base type for every subclass. Name them `from_*` and keep them thin.

code

python · 23 lines
python
import csv
import io


class FlagSet:
    def __init__(self, flags):
        self.flags = dict(flags)


    @classmethod
    def from_rows(cls, text):
        reader = csv.reader(io.StringIO(text))
        return cls({name: state == "on" for name, state in reader})


class AuditedFlagSet(FlagSet):
    pass


rows = "checkout_v2,on\nnew_pricing,off\n"
print(type(FlagSet.from_rows(rows)).__name__)         # FlagSet
print(type(AuditedFlagSet.from_rows(rows)).__name__)  # AuditedFlagSet
print(FlagSet.from_rows(rows).flags)                  # {'checkout_v2': True, 'new_pricing': False}

go deeper

for a junior

Know that a classmethod's first argument is the class and that returning cls(...) creates an instance. Recognise the from_* naming convention and be able to point at a stdlib example such as dict.fromkeys.

for a middle

Explain that cls is bound at call time to the looked-up class, and show with a two-class example why a static factory that names the class returns the wrong type for subclasses. Keep init canonical and the factory thin.

for a senior

Demonstrate the design judgement: how many named constructors a type should carry, the constructor-signature coupling an inherited factory creates across a hierarchy, and when a module-level function is simply the better answer.

for a principal

Own the contract this sets for everyone subclassing your types: whether subclass constructors are required to stay signature-compatible, how factories interact with serialization and configuration boundaries, and where a builder or a parsing layer should replace a growing pile of from_* methods.

### The problem it solves `__init__` should accept one canonical set of arguments: the pieces the object genuinely needs. Real code, though, builds the same object from several sources — a mapping, a decoded row set, a timestamp, a file. Cramming all of those into `__init__` produces mutually exclusive parameters and a stack of `if` branches that must be re-read every time anyone touches the type. The Python idiom is to leave `__init__` alone and add a named classmethod per input format: `from_rows`, `from_mapping`, `fromkeys`, `fromisoformat`. Each one parses its own format and then delegates to `cls(...)`. The standard library does exactly this — `dict.fromkeys`, `datetime.date.today`, `datetime.date.fromisoformat`, `datetime.datetime.fromtimestamp` — so the shape is instantly recognisable to any Python reader. ### Why `@classmethod` rather than `@staticmethod` The decisive property is that `cls` is bound at call time to **the class the attribute was looked up on**, not to the class in whose body the `def` appeared. Write `return cls(...)` and the factory becomes polymorphic for free: ```python class FlagSet: @classmethod def from_rows(cls, text): return cls(parse(text)) class AuditedFlagSet(FlagSet): pass type(AuditedFlagSet.from_rows(data)) # AuditedFlagSet ``` The same factory written as a `@staticmethod` must name a class in its body. `return FlagSet(...)` is frozen at the moment it was typed, so `AuditedFlagSet.from_rows(...)` hands back a plain `FlagSet` — no exception, no warning, just the wrong type flowing downstream until something notices. In a feature-flag service where the audited subclass is the one that records who toggled what, that silent downgrade means the audit trail is simply empty for objects built through the factory, across all 6,800 flag rows in the batch, and nothing in the logs points at the factory. ### What `cls(...)` actually does `cls(...)` is the same construction call as writing the class name and parentheses. It runs the class's normal construction path, including whatever `__init__` the *subclass* defines. That is a real constraint on the idiom: if a subclass changes its constructor signature, an inherited classmethod that calls `cls(a, b)` will break. There are two honest answers. Either keep subclass constructor signatures compatible — the usual choice, and a reason to keep `__init__` parameter lists small — or have the subclass override the classmethod with one that matches its own constructor. ### Calling it through an instance `obj.from_rows(...)` is legal and passes `type(obj)`; the instance itself is discarded. This surprises people who expect it to behave like an instance method, and it is a genuine design signal: if a factory needs to read attributes of an existing instance, it is not a classmethod, it is an instance method (something like `copy_with` or `replace`). ### Conventions that make the idiom readable - **Name them `from_*`.** The prefix is a strong convention. `dict.fromkeys` and `datetime.date.fromisoformat` show it is used without the underscore in the stdlib too, but new code should prefer `from_rows`, `from_mapping`. - **Keep them thin.** Parse or adapt, then hand off to `cls(...)`. A classmethod containing the real initialization logic duplicates `__init__` and drifts from it. - **Return `cls(...)`, never the class name.** This is the single line that makes the difference and the one interviewers check. - **Do not swallow validation.** The classmethod's job is to translate one input format into constructor arguments; the constructor still enforces the invariants. ### When a plain function is the better answer Not everything must be a classmethod. For a single concrete class with no subclasses in sight, a module-level `load_flags(path) -> FlagSet` is perfectly idiomatic, easier to import, and trivially testable. The classmethod earns its place when inheritance is real (`cls` follows the subclass), when the factory belongs to the type's public surface for discoverability, or when subclasses need to override the construction step. Reaching for `@classmethod` reflexively for every helper that returns an instance is over-engineering; refusing to use it in a hierarchy is a bug waiting to happen. ### Version note The idiom itself is version-independent and works the same way on 3.14. One nearby change: chaining `classmethod` around another descriptor — the trick that briefly allowed class-level computed attributes from 3.9 — was deprecated in 3.11 and removed in 3.13, so it is gone on 3.14. Ordinary alternative-constructor classmethods are unaffected.

  • What breaks when a subclass changes its __init__ signature but inherits the classmethod factory?
    The factory breaks, and that is the known cost of the idiom: `cls(...)` calls whatever constructor the subclass defines, so an inherited `cls(a, b)` fails against a subclass expecting different arguments. Either keep subclass constructor signatures compatible, which is a good reason to keep parameter lists small, or have the subclass override the classmethod with one matching its own constructor.
  • Does a classmethod called through an instance still receive the class?
    Yes. `obj.from_rows(...)` passes `type(obj)` and discards the instance, so the classmethod cannot read instance attributes. That trips up people who expect instance-method behaviour, and it is a useful design signal: if a factory needs an existing instance's state, it should be an instance method such as `replace` or `copy_with`.
  • When is a module-level factory function the better choice?
    When there is one concrete class, no subclassing in sight, and the construction step needs no overriding. A module-level `load_flags(path)` imports cleanly and tests easily. The classmethod earns its place when inheritance is real, when the factory belongs on the type's public surface for discoverability, or when subclasses must be able to override how the object is built.

saying these in an interview costs you the question

  • Returns a hardcoded class name instead of cls(...)
  • Says cls is the class where the method was defined
  • Thinks a classmethod can also read instance attributes
  • Overloads __init__ with mutually exclusive parameters instead
  • Claims classmethods cannot be inherited or overridden

context