[Python] Different number of __init__ calls for almost the same __new__ implementation

Discussão em 'Python' iniciado por Stack, Abril 8, 2025.

  1. Stack

    Stack Membro Participativo

    My goal is to create a Factory class that will produce instances of slightly different classes based on real_base parameter. For simplicity I pass new base class directly, in reality base class would have been determined based on input parameter with some complex logic. The problem is that if I use same class as base, I get different set of methods being invoked compared to using another class as base.

    class SomeClass:
    def __init__(self, *args, **kwargs):
    print(f"{self.__class__.__name__}.__init__ got {args=}, {kwargs=}")

    def __new__(cls, real_base=None, *args, **kwargs):
    print(f"{cls.__name__}({__class__.__name__}).__new__ got {real_base=}, {args=}, {kwargs=}")

    if not real_base: # If there is not real_base - act like regular class
    return super().__new__(cls, *args, **kwargs)

    # Otherwise - act like class factory
    new_field_class = type(cls.__name__ + 'Custom', (real_base, ), {})
    print(f"{cls.__name__} factory call")
    res = new_field_class()
    print(f"{cls.__name__} factory call returned {res} {res.__class__.mro()}")
    return res

    class OtherClass:
    def __init__(self, *args, **kwargs):
    print(f"{self.__class__.__name__} __init__ got {args=}, {kwargs=}")

    def __new__(cls, *args, **kwargs):
    print(f"{cls.__name__}({__class__.__name__}) __new__ got {args=}, {kwargs=}")
    return super().__new__(cls, *args, **kwargs)


    Here main logic is in SomeClass.__new__, while rest of methods just use print to show if they are getting called.

    If I use this classes as follows:

    SomeClass(OtherClass)
    print()
    SomeClass(SomeClass)


    I get the following output:

    SomeClass(SomeClass).__new__ got real_base=<class '__main__.OtherClass'>, args=(), kwargs={}
    SomeClass factory call
    SomeClassCustom(OtherClass) __new__ got args=(), kwargs={}
    SomeClassCustom __init__ got args=(), kwargs={}
    SomeClass factory call returned <__main__.SomeClassCustom object at 0x104fcfa10> [<class '__main__.SomeClassCustom'>, <class '__main__.OtherClass'>, <class 'object'>]

    SomeClass(SomeClass).__new__ got real_base=<class '__main__.SomeClass'>, args=(), kwargs={}
    SomeClass factory call
    SomeClassCustom(SomeClass).__new__ got real_base=None, args=(), kwargs={}
    SomeClassCustom.__init__ got args=(), kwargs={}
    SomeClass factory call returned <__main__.SomeClassCustom object at 0x104fcfa40> [<class '__main__.SomeClassCustom'>, <class '__main__.SomeClass'>, <class 'object'>]
    SomeClassCustom.__init__ got args=(<class '__main__.SomeClass'>,), kwargs={}


    I understand why 3rd line is different - it literally calls different __new__ implementation.
    What I don't understand is - why in second example there is an extra __init__ call ?

    Continue reading...

Compartilhe esta Página