Intermediate Python 📂 Class and Object · 9 of 10 37 min read

Python OOP — new, call, and the Singleton Pattern Explained

Master three tightly-linked advanced OOP topics: new (the constructor that runs before init), call (makes instances callable like functions), and the Singleton pattern (ensures only one instance ever exists). Learn through a two-step-birth analogy, three animated SVG diagrams (creation lifecycle, callable dispatch, singleton reuse), Config-Singleton and callable Counter/Validator examples, plus 10 golden rules.

Section 01

The Story That Explains These Three Ideas

The Two-Step Birth of an Object
Think about how a car is made. It's two steps, always:

Step 1 — build the chassis. The factory line rolls out a steel body. It's a real car now, but empty inside. That's __new__ — it creates the object.

Step 2 — customise it. Painters add colour, workers install seats and dashboard. That's __init__ — it initialises the freshly-created object.

So every time you write Car("Honda"), Python runs __new__ to build the empty car, then __init__ to fill in "Honda" as the brand.

Two special twists follow. First: some things exist only once — the Sun, the company CEO, the app configuration. Ask for one a hundred times and you get the same one every time. That's the Singleton pattern — you customise __new__ to always hand back the same instance. Second: some objects act like verbs — you press them and they do something. A horn is an object, but horn() makes a sound. That's __call__ — it makes your instance itself callable, just like a function.
🔮
The Core Insight

__new__ creates the object. __init__ fills it in. __call__ lets the object be called like a function. Combine them and you get singletons (one instance, always reused) and callable objects (verbs in class form). Three tools, one connected family.


Section 02

__new__ vs __init__ — The Full Lifecycle

When you write Person("Alice", 30), two methods run in order. Almost every Python class only needs __init__ — Python's default __new__ handles the creation invisibly. Understanding both is what unlocks singletons, immutable subclasses of built-in types, and metaclasses.

🏗️ What Actually Happens on Person("Alice", 30)
Step 1
Python calls Person.__new__(cls, "Alice", 30) — this creates a bare, empty Person object and returns it.
Step 2
Python calls Person.__init__(new_object, "Alice", 30) — this fills in the attributes on the object __new__ just returned.
Step 3
The fully-initialised object is what your variable now points to.
Rule
__new__ receives the class (cls) — it doesn't have a self yet. __init__ receives the object (self) that __new__ just created.
class Person:
    def __new__(cls, *args, **kwargs):
        print(f"  [__new__] creating a new Person object")
        instance = super().__new__(cls)      # let object create it for us
        return instance                          # MUST return the new object

    def __init__(self, name, age):
        print(f"  [__init__] filling in name={name!r}, age={age}")
        self.name = name
        self.age  = age

print("Creating Alice:")
alice = Person("Alice", 30)
print()
print("Creating Bob:")
bob = Person("Bob", 25)
OUTPUT
Creating Alice: [__new__] creating a new Person object [__init__] filling in name='Alice', age=30 Creating Bob: [__new__] creating a new Person object [__init__] filling in name='Bob', age=25

Animated Diagram — The Two-Step Object Creation

Follow Person("Alice", 30). Python fires __new__ first — it produces a bare, unnamed object. Then it fires __init__ on that same object — filling in name and age. The finished object is what your variable receives.

CREATION LIFECYCLE  ·  __new__ → __init__ → ready
Person("Alice", 30) STEP 1  ·  __new__(cls, ...) creates and returns a bare object Person object (empty — no attributes yet) STEP 2  ·  __init__(self, name, age) fills in the object __new__ just built name='Alice', age=30 → your variable points to this finished object

Every Person(...) call runs this two-step pipeline. __new__ builds the empty shell; __init__ customises it.


Section 03

When Do You Actually Override __new__?

99% of Python classes never touch __new__. The default handles it. You reach for it only when you need to control which object is returned, or subclass an immutable built-in type.

🔄
Singleton Pattern
reuse the same instance
Check if an instance already exists; if yes, return it; if not, create one. This is the most common real-world use of __new__.
🔐
Immutable Subclasses
extend str, int, tuple
Subclassing an immutable built-in (str, int, tuple) means __init__ can't set state — the object is already frozen. Do it in __new__ instead.
🏭
Object Pool / Cache
reuse frequent instances
Return an existing object from a pool if the same key is requested. Python's own int class does this for small numbers (integer caching).
⚠️
Two Rules to Remember

__new__ is a static-like method whose first argument is cls (the class), not self. It must return an instance. If it returns something that's not an instance of cls, Python won't call __init__ afterwards.


Section 04

__call__ — Making an Instance Callable

Functions are callable — len(), print(), your own calculate(). Instances are usually not callable. Defining __call__ changes that: your instance can now be invoked with () just like a function.

class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    # ── This makes the instance callable ──
    def __call__(self, x):
        return x * self.factor


double = Multiplier(2)
triple = Multiplier(3)

# Instances now behave like functions
print(double(10))    # 20   ← calls double.__call__(10)
print(triple(10))    # 30   ← calls triple.__call__(10)

# They can even be passed anywhere a function is expected
print(list(map(double, [1, 2, 3])))   # [2, 4, 6]

# They remain OBJECTS — you can inspect and mutate state
print(double.factor)          # 2
double.factor = 5
print(double(10))            # 50   ← same instance, new behaviour
OUTPUT
20 30 [2, 4, 6] 2 50

Animated Diagram — obj() Becomes obj.__call__()

CALLABLE DISPATCH  ·  obj(x) → obj.__call__(x)
WHAT YOU WRITE double(10) Python translates WHAT PYTHON RUNS double.__call__(10) YOUR __call__ RUNS def __call__(self, x):     return x * self.factor 20

Calling an instance with () is really Python calling its __call__ method. Same syntax as functions, but you keep instance state.


Section 05

Why Callable Classes Instead of Just Functions?

🛠️ Plain Function
No memory between calls
Configuration passed every time
Fine for stateless operations
def multiply(x, factor):
🔥 Callable Instance
Carries state across calls
Configured once at construction
Reads like a function, behaves like an object
Multiplier(2) — then reuse forever

Common patterns that benefit from __call__:

  • Configured functions — a validator with rules bound at construction (Validator(min_length=8)), then called as validator("input")
  • Counters and accumulators — instance carries the running total between calls
  • Decorators as classes@Retry(max_attempts=3) — the decorator's __call__ wraps the function
  • Pipelines / transformsPipeline(steps) then pipeline(data)
  • Fluent APIs — chainable calls that also carry inspectable state

Section 06

The Singleton Pattern — One Instance, Reused Forever

A singleton is a class that only ever produces one instance. No matter how many times you call Config(), you get the same Config object every time. Perfect for app-wide state — configuration, database connections, logging, caches.

class Config:
    _instance = None                       # class variable — the "one and only" slot

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            print("  [__new__] first call — creating THE instance")
            cls._instance = super().__new__(cls)
        else:
            print("  [__new__] already exists — returning the same instance")
        return cls._instance

    def __init__(self, filename=None):
        # NOTE: __init__ runs on EVERY Config(...) call — beware of clobbering
        if filename is not None:
            self.filename = filename


print("First call:")
a = Config("app.ini")

print("\nSecond call:")
b = Config()

print("\nThird call:")
c = Config("override.ini")

print()
print("a is b:", a is b)           # True — literally the same object
print("b is c:", b is c)           # True
print("a.filename:", a.filename)   # override.ini (the last __init__ clobbered it)
OUTPUT
First call: [__new__] first call — creating THE instance Second call: [__new__] already exists — returning the same instance Third call: [__new__] already exists — returning the same instance a is b: True b is c: True a.filename: override.ini
🔥
The __init__ Gotcha

__init__ runs on every Config(...) call — even the ones where __new__ returned the existing instance. That means calling Config("override.ini") a second time overwrites the filename set the first time. Guard against re-init explicitly if you don't want that (see the next section for the "initialized flag" pattern).

Animated Diagram — All Three Calls Return the Same Object

SINGLETON  ·  ONE INSTANCE, EVERY CALL RETURNS IT
CALL 1 a = Config() CALL 2 b = Config() CALL 3 c = Config() THE ONE INSTANCE Config._instance at 0x7f8a2c1d4890 every call returns this same object a is b is c   →   True not three objects — the same object bound to three names

Three different calls (a, b, c), one memory address. __new__ checks the slot, hands back the existing object, skips creation.


Section 07

A Safer Singleton — Guard __init__ Too

Because __init__ runs on every construction call, you often want a flag to run the actual setup only once. This is the "initialised once" pattern used in most real singletons.

class ConfigManager:
    _instance = None
    _initialized = False

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, filename="app.ini"):
        # Guard: don't re-run on subsequent construction calls
        if ConfigManager._initialized:
            return
        print(f"Loading {filename} — this runs ONCE")
        self.filename = filename
        self.settings = {"debug": True, "version": "2.4"}
        ConfigManager._initialized = True


a = ConfigManager("app.ini")             # prints "Loading app.ini..."
b = ConfigManager("different.ini")       # silent — already initialised

print(a.filename)                        # app.ini (still the first value)
print(a is b)                            # True
OUTPUT
Loading app.ini — this runs ONCE app.ini True

Section 08

Alternative Singleton Patterns

The __new__ approach is the most obvious, but Python offers several equivalent patterns. Each fits a different taste in code style.

Pattern 1 — Decorator

def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance


@singleton
class Database:
    def __init__(self, host):
        self.host = host

db1 = Database("localhost")
db2 = Database("other-host")
print(db1 is db2)   # True
print(db1.host)     # localhost (the second call was ignored)

Pattern 2 — Module-Level (the Pythonic default)

# In config.py — module itself IS the singleton
settings = {"debug": True, "version": "2.4"}

def get(key):
    return settings.get(key)

# Anywhere else:
# from config import settings, get
# Every import gets the same module → same settings dict — natural singleton
💡
The Pythonic Truth

In many real Python codebases, the "singleton" is just a module. Python caches imported modules once, so import config in ten different files gives every file the same config object. No __new__, no decorator, no metaclass — just a plain module. Reach for __new__-based singletons only when you genuinely need class semantics (subclassing, encapsulation of behaviour).


Section 09

Practical Example — A Callable Counter With State

A class that acts like a function but remembers how many times it's been called. Classic __call__ use case.

class Counter:
    def __init__(self, start=0):
        self.count = start

    def __call__(self):
        self.count += 1
        return self.count

    def reset(self):
        self.count = 0

    def __repr__(self):
        return f"Counter(count={self.count})"


c = Counter()
print(c())     # 1  ← invoked like a function
print(c())     # 2
print(c())     # 3

# Still an object — you can inspect and reset
print(c)       # Counter(count=3)
c.reset()
print(c())     # 1 (started over)

# Multiple independent counters — each carries its own state
hits    = Counter()
misses  = Counter()
hits(); hits(); hits()
misses()
print(f"hits={hits.count}, misses={misses.count}")   # hits=3, misses=1
OUTPUT
1 2 3 Counter(count=3) 1 hits=3, misses=1

Section 10

Practical Example — A Callable Validator

Combine __call__ with __init__-time configuration to build small, reusable validation objects. Configure once, call anywhere.

class MinLengthValidator:
    def __init__(self, min_length):
        self.min_length = min_length

    def __call__(self, value):
        # Uses config from __init__ every time it's called
        if len(value) < self.min_length:
            raise ValueError(
                f"{value!r} must be at least {self.min_length} chars"
            )
        return value


# Build once, use everywhere
username_valid = MinLengthValidator(3)
password_valid = MinLengthValidator(8)

# Call the instances like functions
print(username_valid("alice"))       # 'alice'
print(password_valid("SecureP@ss"))  # 'SecureP@ss'

try:
    password_valid("abc")
except ValueError as e:
    print("Blocked:", e)

# Callables can be passed to higher-order functions
inputs = ["alice", "bob", "chandra"]
valid_names = [n for n in inputs if len(n) >= username_valid.min_length]
print(valid_names)
OUTPUT
alice SecureP@ss Blocked: 'abc' must be at least 8 chars ['alice', 'bob', 'chandra']

Section 11

Common Mistakes (and Fixes)

MistakeWhat Goes WrongFix
Forgetting to return from __new__ Person(...) returns None because __new__ returned nothing Always end __new__ with return cls._instance (or similar)
Passing self as first parameter to __new__ __new__ takes cls, not self — the object doesn't exist yet Use def __new__(cls, ...): — never self
Setting attributes in __new__ that get clobbered by __init__ Whatever you set in __new__ is overwritten by __init__ right after Set state in __init__. Use __new__ only for creation and identity
Singleton with mutable state, no re-init guard Second call's __init__ silently overwrites the first call's config Add an _initialized flag and short-circuit __init__ if already true
Singleton in multi-threaded code without a lock Two threads may both see _instance is None and create two instances Guard __new__ with threading.Lock(), or use module-level singleton (which is thread-safe on import)
Making __call__ mutate self when callers expect purity Callers assume obj(x) is like fn(x) — surprising side effects break trust Either document the side effect clearly, or use a separate mutating method
Using a singleton for testability-critical state Tests can't easily reset or replace the singleton — flaky tests Prefer dependency injection over singletons where possible; reset in test fixtures

Section 12

Quick Reference

TaskSyntaxNotes
Override object creationdef __new__(cls, *a, **kw):First arg is cls, must return an instance
Delegate default creationsuper().__new__(cls)Standard way to create the bare object
Fill in attributesdef __init__(self, ...):Runs AFTER __new__
Make instance callabledef __call__(self, ...):Then use obj(args) like a function
Basic singletonStore _instance class var, check in __new__Simplest and most common pattern
Safe singletonAdd _initialized flag, guard __init__Prevents re-init on subsequent calls
Singleton via decoratorWrap class in a factory functionCleaner syntax, no __new__ override
Pythonic singletonJust use a moduleModules are cached — natural singletons
Check if callablecallable(obj)True if the object defines __call__
Check identitya is bTrue if same object in memory

Section 13

Golden Rules

🏦 __new__, __call__ & Singletons — Non-Negotiable Rules
1
Every ClassName(...) call runs two methods: __new__ creates the object, __init__ fills it in. Understand this order — every singleton, immutable subclass, and metaclass depends on it.
2
Don't override __new__ unless you have a reason. The default is right 99% of the time. Reach for it only for singletons, immutable subclasses of built-ins, or object pools.
3
__new__ takes cls, not self — the object doesn't exist yet. And it must return an instance. Forgetting the return produces the classic NoneType has no attribute... bug.
4
__init__ runs every time you call ClassName(...) — even when __new__ returned an existing instance. For singletons, guard __init__ with an _initialized flag so config isn't clobbered.
5
__call__ makes an instance callable with obj(x). Use it for configured functions (Validator(min=8), Retry(n=3)) and stateful accumulators (counters, caches). If the object never carries state, a plain function is clearer.
6
A singleton is a class that only ever produces one instance. Store it as a class variable (_instance), check in __new__, hand back the same object every time. Test with a is b — identity, not equality.
7
Prefer a Python module over an explicit singleton when you can. Modules are imported exactly once and cached — from config import settings gives you a natural, thread-safe singleton with zero ceremony.
8
Singletons in multi-threaded code need a lock. Two threads both seeing _instance is None at the same time will both create instances. Wrap __new__'s check in threading.Lock(), or use module-level state.
9
Singletons hurt testability. They're global state in disguise — hard to reset, hard to substitute for tests. Prefer dependency injection when the object is used in code you'll want to test.
10
When in doubt, ask three questions. Do I need to customise creation?__new__. Do I want the instance to be callable like a function?__call__. Do I need exactly one instance across the app? → singleton (or a module). Match the tool to the shape of the problem.