Skip to content
Happy Programming Guide
Start learning
Python

Understanding the Python __new__ Method Explained

What __new__ does, how it differs from __init__, and the three cases where you genuinely need it: singletons, immutable subclasses and instance caching.

Books and a notebook on a desk

__new__ creates the object; __init__ fills it in. Python calls __new__ first, and whatever it returns is the instance that __init__ is then handed. Almost all classes only need __init__ — but there are three situations where you cannot avoid __new__, and this guide covers what they are and how to write it correctly.

The order of events#

Python
class Demo:
    def __new__(cls, *args, **kwargs):
        print("1. __new__ - creating the object")
        instance = super().__new__(cls)
        return instance

    def __init__(self, value):
        print("2. __init__ - setting it up")
        self.value = value


d = Demo(42)
Output
1. __new__ - creating the object
2. __init__ - setting it up

Three differences worth memorising:

__new__ __init__
First parameter cls (the class) self (the instance)
Returns the new instance nothing (None)
Kind of method static, implicitly ordinary method

Case 1: subclassing an immutable type#

This is the situation you are most likely to meet for real. Immutable types are fully built by the time __init__ runs, so setting the value there is too late:

Python
class Distance(float):
    def __init__(self, metres):
        self = metres * 1000      # does nothing useful


print(Distance(5))    # 5.0, not 5000.0

The value has to be set at creation time:

Python
class Distance(float):
    def __new__(cls, metres):
        return super().__new__(cls, metres * 1000)

    def __init__(self, metres):
        self.metres = metres      # extra attributes are still fine here


d = Distance(5)
print(d)            # 5000.0
print(d.metres)     # 5
print(d + 100)      # 5100.0 - still a real float

The same applies to str, int, bytes, tuple and frozenset:

Python
class Upper(str):
    def __new__(cls, text):
        return super().__new__(cls, text.upper())


print(Upper("hello"))          # HELLO
print(Upper("hello").lower())  # hello - all str methods still work

Case 2: singletons#

One instance, shared by every caller:

Python
class Settings:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._loaded = False
        return cls._instance

    def load(self, values):
        self.values = values
        self._loaded = True


a = Settings()
b = Settings()
print(a is b)      # True - the same object

There is a catch. __init__ runs on every call, even when __new__ returned an existing instance:

Python
class Counter:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        self.count = 0        # resets every time Counter() is called


c1 = Counter()
c1.count = 99
c2 = Counter()
print(c1.count)   # 0 - wiped out

Guard it:

Python
    def __init__(self):
        if getattr(self, "_ready", False):
            return
        self.count = 0
        self._ready = True

Case 3: caching instances#

When the same arguments should always give you the same object:

Python
class Colour:
    _cache = {}

    def __new__(cls, name):
        key = name.lower()
        if key not in cls._cache:
            instance = super().__new__(cls)
            instance.name = key
            cls._cache[key] = instance
        return cls._cache[key]


print(Colour("Red") is Colour("red"))   # True
print(len(Colour._cache))               # 1

This is how small integers and short strings behave in CPython, and how bool guarantees there is only ever one True.

Returning something else entirely#

__new__ may return an object of a different class. When it does, __init__ is not called at all:

Python
class Shape:
    def __new__(cls, sides):
        if cls is Shape:
            if sides == 3:
                return super().__new__(Triangle)
            if sides == 4:
                return super().__new__(Square)
        return super().__new__(cls)


class Triangle(Shape):
    pass


class Square(Shape):
    pass


print(type(Shape(3)))   # Triangle
print(type(Shape(4)))   # Square

The if cls is Shape check matters — without it, constructing a Triangle directly would recurse.

When you do not need it#

Nearly always. If you are only setting attributes, validating arguments or calling a parent constructor, __init__ is the right place. Reach for __new__ only when:

  • you are subclassing an immutable built-in type
  • you must control whether a new object is created at all
  • you need to return a different class from the constructor

Two lighter alternatives worth knowing: a classmethod named something like from_string is clearer than overloading construction, and functools.lru_cache on a factory function handles caching without touching object creation.

Questions people ask#

Is __new__ a static method?

Yes, implicitly — Python treats it as one even without the decorator. That is why its first parameter is cls and why you pass the class explicitly when calling super().__new__(cls).

Can __new__ take arguments?

Yes, and it receives the same arguments as __init__. Accept *args, **kwargs if you do not need them, so subclasses with different signatures do not break.

What about dataclasses?

Dataclasses generate __init__ and leave __new__ alone. If you need custom creation, define __new__ yourself; it works alongside the generated __init__.

Do I need __new__ for a metaclass?

Different level. A metaclass’s __new__ creates the class, not the instance. If you are asking the question, you almost certainly do not need a metaclass yet.

Where to go next#

Object-oriented programming in Python, from the beginningRead next

Keep reading

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *