Factory Design Pattern in Python (With Examples)

The factory design pattern in Python means letting a function (or class method) decide which class to create, so your code asks for an object by name, not by class. In Python, a dictionary of classes is often all you need:

METHODS = {"ground": Ground, "express": Express}

def make_shipping(method):
    return METHODS[method]()

Each pattern below ran on Python 3.12.5, using shipping, notifications, orders and payments as examples.

I’ll show a simple factory, a self-registering factory with a decorator, factory methods with @classmethod, abstract base classes that enforce the interface, and when a factory is overkill.

A simple factory in Python

The caller passes a name, and make_shipping() returns the right object. Every class has the same cost() method, so the caller doesn’t care which one it got:

class Ground:
    def cost(self, pounds):
        return 5.99 + 0.50 * pounds

class Express:
    def cost(self, pounds):
        return 12.99 + 1.25 * pounds

class Overnight:
    def cost(self, pounds):
        return 24.99 + 2.00 * pounds

METHODS = {"ground": Ground, "express": Express, "overnight": Overnight}

def make_shipping(method):
    """The factory: callers ask by name and never import the classes."""
    try:
        return METHODS[method.lower()]()
    except KeyError:
        raise ValueError(f"unknown shipping method {method!r}; choose from {sorted(METHODS)}") from None

for name in ["ground", "Express", "overnight"]:
    shipper = make_shipping(name)
    print(f"{name:<9} {type(shipper).__name__:<9} 4 lb -> ${shipper.cost(4):.2f}")

try:
    make_shipping("drone")
except ValueError as err:
    print("ValueError:", err)

Output:

ground    Ground    4 lb -> $7.99
Express   Express   4 lb -> $17.99
overnight Overnight 4 lb -> $32.99
ValueError: unknown shipping method 'drone'; choose from ['express', 'ground', 'overnight']
Command Prompt output of a Python simple factory creating Ground, Express and Overnight shipping objects and a ValueError for an unknown method
Callers never touch the classes directly, and a bad name gets a clear error listing the valid options.

Adding a new shipping option means adding one class and one dictionary entry. No if/elif chain anywhere else needs to change.

A self-registering factory with a decorator

For plugin-style code, let each class register itself. A small decorator adds the class to the factory’s dictionary when it’s defined:

NOTIFIERS = {}

def register(name):
    def decorator(cls):
        NOTIFIERS[name] = cls             # each class adds itself to the factory
        return cls
    return decorator

@register("email")
class EmailNotifier:
    def send(self, to, msg):
        return f"email to {to}: {msg}"

@register("sms")
class SmsNotifier:
    def send(self, to, msg):
        return f"text to {to}: {msg[:20]}"

def create_notifier(kind):
    return NOTIFIERS[kind]()

print("registered:", list(NOTIFIERS))
print(create_notifier("email").send("ava@example.com", "Your order shipped"))
print(create_notifier("sms").send("512-555-0147", "Your order shipped today, track it online"))

Output:

registered: ['email', 'sms']
email to ava@example.com: Your order shipped
text to 512-555-0147: Your order shipped t
Command Prompt output of a Python factory using a registration decorator to create email and SMS notifier objects
Both notifier classes registered themselves, so the factory knows about them without a hand-written dictionary.

Decorators like this one are covered in Python class decorators.

Factory methods with @classmethod

A factory method is a class method that builds an instance from some other format. Python uses this all over the standard library, such as dict.fromkeys() and date.fromisoformat():

from datetime import date

class Order:
    def __init__(self, order_id, total, placed):
        self.order_id, self.total, self.placed = order_id, total, placed

    @classmethod
    def from_csv(cls, line):                     # factory method: build from a CSV row
        order_id, total, placed = line.split(",")
        return cls(int(order_id), float(total), date.fromisoformat(placed))

    @classmethod
    def from_dict(cls, data):                    # factory method: build from JSON-style data
        return cls(data["id"], data["total"], date.fromisoformat(data["date"]))

    def __repr__(self):
        return f"Order({self.order_id}, ${self.total:.2f}, {self.placed})"

print(Order.from_csv("1042,59.99,2026-11-10"))
print(Order.from_dict({"id": 1043, "total": 120.0, "date": "2026-11-11"}))

Output:

Order(1042, $59.99, 2026-11-10)
Order(1043, $120.00, 2026-11-11)
Command Prompt output of Python classmethod factory methods creating Order objects from a CSV row and a dictionary
Two ways to build the same kind of object, each with a descriptive name.

Returning cls(...) rather than Order(...) means subclasses get the factory methods for free. More in the Python classmethod guide.

How do you enforce the interface in a factory?

If every product must have the same methods, define an abstract base class. Python then refuses to create a subclass that forgot to implement them:

from abc import ABC, abstractmethod

class PaymentMethod(ABC):
    @abstractmethod
    def charge(self, amount): ...

class Card(PaymentMethod):
    def charge(self, amount):
        return f"charged ${amount:.2f} to card"

class BankTransfer(PaymentMethod):
    def charge(self, amount):
        return f"scheduled ${amount:.2f} bank transfer"

class Broken(PaymentMethod):          # forgot to implement charge()
    pass

def payment_factory(kind) -> PaymentMethod:
    return {"card": Card, "bank": BankTransfer, "broken": Broken}[kind]()

print(payment_factory("card").charge(49.99))
print(payment_factory("bank").charge(1250.00))
try:
    payment_factory("broken")
except TypeError as err:
    print("TypeError:", err)

Output:

charged $49.99 to card
scheduled $1250.00 bank transfer
TypeError: Can't instantiate abstract class Broken without an implementation for abstract method 'charge'
Command Prompt output of a Python factory returning payment method objects, with a TypeError for a class missing its abstract method
Broken didn’t implement charge(), so creating it fails right away instead of later in production.

See abstract base classes in Python for the details of abc.

When to use the factory pattern (and when not to)

Use a factory whenSkip it when
The class to create depends on data (a config value, user choice, file type)There’s only one class
You keep adding new typesThe types never change
Callers shouldn’t import every concrete classA direct constructor call is clearer
You need several ways to build one classOne __init__ covers it

In Python I keep factories small: a dict, a function, or a classmethod. The heavy class hierarchies you see in Java-style examples are rarely needed.

More Python object-oriented guides:

Frequently asked questions

What is the factory design pattern in Python?

It’s a way to create objects without naming their class directly: a function or method picks the right class and returns an instance.

What’s the simplest factory in Python?

A dictionary that maps names to classes, plus a function that looks up the name and calls the class.

What’s the difference between a factory function and a factory method?

A factory function stands alone and can return different classes. A factory method is a @classmethod that builds instances of its own class from other data.

How do I handle an unknown type in a factory?

Catch the KeyError and raise a ValueError that lists the valid choices.

Do I need abstract base classes for a factory?

No, but they help: Python raises TypeError if a product class forgets a required method.

Where are abstract base classes documented?

See abc in the Python docs.