What you wrote is essentially correct, there are several errors here and there.
class A(str): def __new__(cls, *args, **kwargs): return str.__new__(cls) def __init__(self, arg01): print(arg01)
This is not entirely correct: if you do not pass any argument to str.__new__ , your new instance will be equivalent to an empty string.
class A(str): def __new__(cls, arg): return str.__new__(cls, arg) def __init__(self, arg): print(arg)
In general, __new__ and __init__ should have the same signature. This is not a requirement, but in this case you need to pass arg to str.__new__ so that you intercept arg .
The myClass.__new__ method is myClass.__new__ . This method will create the myInstance object. __new__ is a real constructor ( __init__ not a constructor!). In the __new__ pseudo code, __new__ look something like this:
The answer of __new__ not to call __init__ , as shown in this simple example:
class C: def __new__(cls): print('entering __new__') self = super().__new__(cls) print('exiting __new__') return self def __init__(self): print('entering __init__') super().__init__() print('exiting __init__') C()
As you can see, in my __new__ I did not call __init__ explicitly, and object.__new__ did not call __init__ .
__init__ automatically called by the Python interpreter itself whenever __new__ returns an instance of the type.
The situation is slightly different when we use immutable types (str, int, float, tuple).
This is not entirely true. The situation is different when we inherit from a type that does not use the default __new__ implementation .
The default implementation of __new__ (i.e., object.__new__ ) is very permissive and ignores every position argument and keyword argument. If you replace it with a less permissive implementation, then there will be problems similar to the ones you observe.
It is important to understand that the problem does not arise not by default __new__ , but by the fact that our __init__ not compatible with __new__ .
foo.__new__(foo, arg = 1)
Did you understand. One bit is wrong: the correct signature for str.__new__ :
def __new__(cls[, object[, encoding[, errors]]])
All arguments except cls are positional and keyword arguments. In fact you can do:
>>> class C(str): ... def __init__(self, object): ... pass ... >>> C('abc') 'abc' >>> C(object='abc') 'abc'
You see? We used a signature for __init__ , which is compatible with str.__new__ , and now we can avoid overriding __new__ !