@classmethod and Alternative Constructors
20 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Real data rarely arrives as the exact arguments __init__ expects. It comes as a text line, a dictionary or a database row. A class method lets the class offer more than one way to build an object, each with a clear name.
Data arrives in other shapes
An order may arrive from a file as one line of text. __init__ wants three separate values.
class Order: def __init__(self, order_id, customer, total): self.order_id = order_id self.customer = customer self.total = total
line = "1001,Anita,2599.50"order_id, customer, total = line.split(",")order = Order(int(order_id), customer, float(total))If every caller splits and converts the text itself, the same code is copied in many places.
Example 1 · Splitting text at every call site
class Order: def __init__(self, order_id, customer, total): self.order_id = order_id self.customer = customer self.total = total
line = "1001,Anita,2599.50"order_id, customer, total = line.split(",")order = Order(int(order_id), customer, float(total))print(order.order_id, order.customer, order.total)1001 Anita 2599.5
What this code does
- The line is split into three strings.
int()andfloat()make the id and total real numbers before the constructor sees them.- This work would be repeated by every caller.
Each place that reads a line must repeat the split and the type conversions. One missed int() and the order id stays text.
@classmethod and cls
Class method is a method marked with @classmethod. Python passes the class itself as the first argument, called cls, instead of an instance, so it can create objects or use class-level data.
Put the parsing inside the class, in a method that receives the class itself.
class Order: def __init__(self, order_id, customer, total): self.order_id = order_id self.customer = customer self.total = total
@classmethod def from_line(cls, line): order_id, customer, total = line.split(",") return cls(int(order_id), customer, float(total))That method is a class method.
Example 2 · from_line as a class method
class Order: def __init__(self, order_id, customer, total): self.order_id = order_id self.customer = customer self.total = total
@classmethod def from_line(cls, line): order_id, customer, total = line.split(",") return cls(int(order_id), customer, float(total))
order = Order.from_line("1002,Ravi,880.00")print(order.order_id, order.customer, order.total)print(type(order).__name__)1002 Ravi 880.0 Order
What this code does
Order.from_line(...)is called on the class. No object exists yet.cls(...)is the same asOrder(...)here, so anOrdercomes back.
Python passes the class as the first argument, named cls by convention. cls(...) then calls the constructor.
Alternative constructors
Alternative constructor is a class method that builds and returns an object from a different kind of input, such as a text line or a dictionary, with a name like from_line or from_dict.
A class method that returns a new object is a factory with its own name. Callers read Order.from_dict(row) and know what it does.
class Order: def __init__(self, order_id, customer, total): self.order_id = order_id self.customer = customer self.total = total
@classmethod def from_dict(cls, row): if "id" not in row: raise ValueError("order id is missing") return cls(row["id"], row.get("customer", "guest"), row["total"])Validation stays in one place.
Example 3 · Two constructors, one validation
class Order: def __init__(self, order_id, customer, total): self.order_id = order_id self.customer = customer self.total = total
@classmethod def from_dict(cls, row): if "id" not in row: raise ValueError("order id is missing") return cls(row["id"], row.get("customer", "guest"), row["total"])
ok = Order.from_dict({"id": 7, "total": 90.0})print(ok.customer)try: Order.from_dict({"total": 10})except ValueError as error: print("Rejected:", error)guest Rejected: order id is missing
What this code does
row.get("customer", "guest")returns"guest"when the key is missing.- The missing id raises
ValueErrorbefore any object is built, so no half-made order exists.
The class keeps one __init__ and gains as many named ways to build an object as it needs.
Why cls and not the class name
Inside a class method, write cls(...), not Order(...).
class Order: def __init__(self, total): self.total = total
@classmethod def from_text(cls, text): return cls(float(text))
class ExpressOrder(Order): passWhen a subclass calls the inherited method, cls is the subclass, so the result has the right type.
Example 4 · cls follows the subclass
class Order: def __init__(self, total): self.total = total
@classmethod def from_text(cls, text): return cls(float(text))
class ExpressOrder(Order): pass
a = Order.from_text("50")b = ExpressOrder.from_text("75")print(type(a).__name__, type(b).__name__)Order ExpressOrder
What this code does
ExpressOrderinheritsfrom_textwithout rewriting it.- Because the method uses
cls, the second call returns anExpressOrder.
A subclass is a class built on another class; it inherits its methods. Hard-coding Order(...) would return a plain Order even for ExpressOrder.from_text.
Class methods and class state
Data that belongs to the class, such as a counter, is shared by all objects. A class method can read and change it because it holds the class.
class Ticket: created = 0
def __init__(self, title): self.title = title
@classmethod def open(cls, title): cls.created += 1 return cls(title)Example 5 · A shared counter
class Ticket: created = 0
def __init__(self, title): self.title = title
@classmethod def open(cls, title): cls.created += 1 return cls(title)
Ticket.open("Login fails")Ticket.open("Invoice missing")print(Ticket.created)2
What this code does
cls.created += 1changes the attribute on the class, so all tickets share it.- Two calls, two tickets, count is 2.
Each call to Ticket.open raises the shared count. No instance is needed to read it.
Next step
Check your understanding
11 quick questions