In Python is het aanmaken van een object van een klasse een proces in twee stappen:
__new__ aangeroepen, die verantwoordelijk is voor het toewijzen en retourneren van een nieuw exemplaar van de klasse (instantie).__init__ aangeroepen, die het reeds aangemaakte object initialiseert.__new__ — creatie (kan elk nieuw object retourneren; verplichte parameter — de klasse).__init__ — initialisatie (werkt met self, het al toegewezen exemplaar).__new__ overschreven voor afgeleiden van onveranderlijke types (tuple, str, int), het maken van singletons en het "factory" patroon.class MyStr(str): def __new__(cls, value): print("__new__ aangeroepen") instance = super().__new__(cls, value.upper()) # waarde veranderen vóór creatie return instance def __init__(self, value): print("__init__ aangeroepen", value) s = MyStr('abc') # __new__ aangeroepen -> __init__ aangeroepen abc print(s) # 'ABC'
Kan je een onveranderlijk object initialiseren via __init__, als er in de klasse geen __new__ is geïmplementeerd?
Nee! Bijvoorbeeld, voor onveranderlijke types (zoals bij str, int, tuple) moeten alle wijzigingen aan velden/waarde in __new__ gebeuren, anders is het in __init__ niet mogelijk om de waarde te wijzigen.
class MyTuple(tuple): def __init__(self, items): print(f'__init__! {items}') def __new__(cls, items): print(f'__new__! {items}') return super().__new__(cls, map(str, items)) t = MyTuple([1, 2, 3]) print(t) # ('1', '2', '3')
Verhaal
In een groot Django-project werd er erfelijkheid van str geïmplementeerd voor het opslaan van speciale typen strings. Ze probeerden de waarde te wijzigen in __init__ — maar het resultaat veranderde niet, omdat str onveranderlijk is. Dit werd pas opgelost na studie en het overschrijven van __new__.
Verhaal
Bij de implementatie van het Singleton patroon vergaten ze de logica voor het retourneren van het bestaande exemplaar in __new__: meerdere aanroepen van de klasse maakten nog steeds nieuwe objecten aan, wat leidde tot geheugenfragmentatie.
Verhaal
In een bibliotheek voor gegevensserialisatie hebben ze bij het cachen een klasse gebruikt met een overschreven __init__, maar ze verzuimden te beseffen dat voor caching het nodig is om precies hetzelfde object te retourneren via __new__. Hierdoor faalde de cache bij elke nieuwe aanroep, omdat er verschillende objecten werden aangemaakt.