__new__ is the class-level method that creates and returns a new instance before __init__ initializes it. Calling a constructor therefore follows two steps: __new__ creates the object, then __init__ receives that same object as self and sets its attributes. section 8.1.1 section 10.2.1
class Task:
def __init__(self):
print(f"__init__ gets called, creating object at {id(self)}")
def __new__(cls):
new_task = object.__new__(cls)
print(f"__new__ gets called, creating object at {id(new_task)}")
return new_task
task = Task()
# output the following lines:
__new__ gets called, creating object at 140702458469952
__init__ gets called, creating object at 140702458469952
Trace the identical object identity through __new__ and __init__ when constructing a Task instance. section 8.1.1
Use __new__ when the creation step itself must differ from ordinary construction:
- Return the newly created instance; unlike __init__, __new__ must return an object that can be initialized and used. section 8.1.1
- Delegate ordinary allocation to object.__new__(cls) or a superclass implementation rather than manually reproducing object creation. section 10.2.1
- Avoid overriding it for routine classes; typical classes need only __init__ for attribute assignment. section 10.2.1
Normalize values in an int subclass
A subclass can override __new__ to convert alternate input before the integer instance exists. PokerOrder maps J, Q, K, and A to 11, 12, 13, and 14, allowing sorted(cards, key=PokerOrder) to produce [1, 10, 'J', 'A']. section 10.5.3


