Back to Blog
Python

python **new** Method: Customizing Object Creation

python **new**: Learn how Python's __new__ method controls object creation, differs from __init__, and supports patterns like singletons and immutable types.

__new__object creationsingleton patternimmutable typesmetaclassinstance allocation
Illustration of Python object creation showing the __new__ method allocating an instance before __init__ initializes it.

Most Python developers rarely think about what happens when they call MyClass(). The interpreter allocates a new instance, initializes it, and returns it. That allocation step is controlled by the __new__ method, and understanding it lets you customize object creation in ways that __init__ alone cannot. The python **new** method is the first step in instance creation, and it is where you decide whether to return a new object or reuse an existing one.

What __new__ Actually Does

__new__ is a static method that receives the class as its first argument and returns a new instance of that class. It is called before __init__, and if it does not return an instance of the class, __init__ is skipped. For most classes, the default implementation from object allocates a fresh instance and returns it. Overriding __new__ gives you control over that allocation.

class Example: def __new__(cls, *args, **kwargs): instance = super().__new__(cls) return instance def __init__(self, value): self.value = value

Here, __new__ explicitly calls super().__new__(cls) to allocate the instance. If you omit this call, no instance is created and __init__ never runs. This is the first thing to understand: __new__ must return an instance of the class, or the object is not created.

When to Override __new__

Overriding __new__ is rarely needed, but it becomes essential in three scenarios:

  • Immutable types: Subclasses of tuple, str, or int cannot modify themselves after creation. __init__ runs after the object exists, so you must set attributes in __new__.
  • Singleton patterns: You want to return the same instance every time the class is called.
  • Custom allocation: You need to control how memory is allocated or return a cached object.

In each case, __init__ is insufficient because it only initializes an already-allocated object. __new__ is the only hook that runs before the object exists.

Overriding __new__ for Immutable Types

Consider a subclass of tuple that needs an extra attribute. Since tuples are immutable, you cannot assign an attribute in __init__; you must do it in __new__ before the object is returned.

class Point(tuple): def __new__(cls, x, y): instance = super().__new__(cls, (x, y)) instance.x = x instance.y = y return instance def __init__(self, x, y): # __init__ is called, but cannot modify the tuple pass

Now Point(1, 2) returns a tuple-like object with x and y attributes. The __init__ method is still invoked, but it does nothing because the attributes were already set. If you tried to set them in __init__, you would get an AttributeError because tuples do not support attribute assignment.

This pattern also applies to str and int subclasses. The key is that __new__ receives the same arguments as the class constructor, and you must pass them to super().__new__ to create the base object correctly.

Implementing a Singleton with __new__

The classic singleton pattern can be implemented entirely in __new__ by controlling whether a new instance is created or an existing one is returned.

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

Every call to Singleton() returns the same object. The _instance attribute is stored on the class, so it is shared across all instances. This approach works for single-threaded code, but for concurrent access you would need a lock to avoid race conditions when two threads create the instance simultaneously.

A more robust version uses a threading.Lock:

import threading class Singleton: _instance = None _lock = threading.Lock() def __new__(cls, *args, **kwargs): if cls._instance is None: with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance

The double-checked locking pattern prevents unnecessary locking after the instance exists. Note that __init__ will still run on every call, so if you need one-time initialization, you must guard it separately.

Using __new__ with Metaclasses

Metaclasses also have a __new__ method, but it operates on the class itself, not on instances. When you define a class, Python calls the metaclass's __new__ to create the class object. This is a different level of control.

class Meta(type): def __new__(mcls, name, bases, namespace): cls = super().__new__(mcls, name, bases, namespace) cls.created_by = "Meta" return cls class MyClass(metaclass=Meta): pass print(MyClass.created_by) # Meta

Here, Meta.__new__ runs when MyClass is defined, not when you create an instance of MyClass. The metaclass __new__ receives the metaclass, class name, bases, and namespace. It returns the new class object. This is useful for class-level validation, registration, or adding methods automatically.

Do not confuse the two. The instance-level __new__ controls object allocation; the metaclass-level __new__ controls class creation. Both are named __new__, but they serve different purposes.

Common Pitfalls and Limitations

Overriding __new__ introduces subtle issues that can break code if you are not careful.

  • Forgetting to return an instance: If __new__ does not return an object of the class, __init__ is skipped and the constructor returns None or whatever you returned. This often leads to confusing errors.
  • Calling super().__new__ with wrong arguments: For immutable types, you must pass the same arguments that the base type expects. For example, tuple.__new__ expects an iterable, not separate coordinates.
  • Ignoring *args and **kwargs: __new__ receives the same arguments as the constructor. If you do not forward them to super().__new__, the base allocation may fail.
  • Returning an instance of a different class: This is allowed but rarely useful. It changes the type of the created object and can break isinstance checks.

A common mistake is to override __new__ and then also try to initialize attributes in __init__. For mutable classes, that works, but for immutable classes it does not. Always consider whether you actually need __new__ or whether a simple __init__ is sufficient.

Performance and Maintainability Considerations

__new__ adds a layer of indirection that can affect performance and readability. Each call to a class with a custom __new__ involves an extra method call, and if you add locking or complex logic, the overhead grows. For most applications, this is negligible, but in hot paths where millions of objects are created, the cost can matter.

Maintainability is a bigger concern. Custom __new__ implementations are often non-obvious to other developers. A singleton implemented with __new__ hides the fact that you are reusing an instance, which can confuse debugging and testing. Prefer explicit factory functions or dependency injection when the pattern is not essential.

If you do use __new__, document why it is necessary. The method is a powerful tool, but it should be reserved for cases where __init__ cannot achieve the goal. For immutable types, there is no alternative. For singletons, a module-level variable or a classmethod may be simpler. For custom allocation, __new__ is the only option.

Understanding the runtime behavior of __new__ also helps when you debug memory issues or object lifecycle problems. Knowing that __init__ is not called when __new__ returns a non-class instance can save hours of investigation. The key is to recognize when the default behavior is sufficient and when you need to intervene at the allocation step.

python **new** Method: Customizing Object Creation | RYUSLOG DEV