2

I have a class A:

class A:
    def __new__(*args, **kwargs)
    def __init__(a1, a2)

Now I'd like to pass in a new argument a3 to create factory

class A:
    def __new__(*args, **kwargs):
       # Do sth with a3

    def __init__(a1, a2)

So here a3 is only used in __new__, but I realized I must pass in a3 into __init__ first to get it work, so that I need to modify __init__ into def __init__(a1, a2, a3) or def __init__(a1, a2, **kwargs). It's weird that I pass a3 but never use it in __init__

So basically here is there anyway I could just trigger __new__ without changing __init__?

LookIntoEast
  • 8,048
  • 18
  • 64
  • 92
  • 1
    Why are you using both `__new__` and `__init__`? Things go smoothest if you stick with one or the other. – user2357112 Jan 18 '22 at 03:21
  • 1
    [this question](https://stackoverflow.com/questions/674304/why-is-init-always-called-after-new) recommends that you use a factory function *instead* of `__new__`. It shouldn't be the way to implement the factory. – Barmar Jan 18 '22 at 03:23
  • Related: https://stackoverflow.com/questions/2468112/python-override-init-args-in-new – Barmar Jan 18 '22 at 03:26
  • Post an actual [mcve] please. I am having a bit of trouble following your made-up pseudocode – Mad Physicist Jan 18 '22 at 03:55

1 Answers1

3

__new__ and __init__ are called by type.__call__. Normally, when __new__ returns an instance of the target class, the metaclass calls __init__ on the result. You can override this behavior by changing the metaclass:

class MetaA(type):
    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls, *args, **kwargs)
        # you can omit `__init__` entirely, or you can replicate
        # what `type` does, but strip the last positional argument:
        if isinstance(obj, cls):
            if len(args) > 2:
                args = args[:2]
            cls.__init__(obj, *args, **kwargs)
        return obj

class A(metaclass=MetaA):
    ...
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264