0

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 ?

asked Apr 8, 2025 at 21:13
8
  • 4
    If __new__() returns an instance of the containing class (including subclasses), then __init__() also gets called - even if the instance was already initialized, due to the nonstandard way you created it (by directly calling the class, rather than calling the inherited __new__(). Commented Apr 8, 2025 at 21:20
  • 2
    As an aside, if you are going to do this (almost certainly, you should do it in a function, not in the __new__ of another class) then you should definitely cache new_field_class = type(cls.__name__ + 'Custom', (real_base, ), {}) Commented Apr 8, 2025 at 21:23
  • Why is SomeClass a class at all, and if there is a reason for it to be a class, why is it easier to use it to define instances of new classes than to create an instance of SomeClass itself? Commented Apr 9, 2025 at 11:29
  • 1
    Then define a new factory function. Why does SomeClass have to do both? Why does it even make sense to try to make it do both? Commented Apr 9, 2025 at 14:59
  • 1
    "So much complexity in software comes from trying to make one thing do two things" Commented Apr 9, 2025 at 15:02

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.