from fractions import Fraction
class F1(Fraction):
def __init__(self, *args, **kwargs):
Fraction.__init__(*args, **kwargs)
class F2(Fraction):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Fraction(1, 10) # Fraction(1, 10)
F1(1, 10) # F1(1, 10)
F2(1, 10) # TypeError: object.__init__() takes exactly one argument (the instance to initialize)
How does this happen? Could someone elaborate a bit on the super function?
Python version: 3.8.10
TLDR;
Fractionuses__new__instead of__init__.Python objects are assembled in two steps. First, they are created with
__new__and then they are initialized with__init__. Usually we use the defaultobject.__new__to create a new empty object and then override__init__for anything special that needs to be done.But some classes want to control the object creation step by implementing their own
__new__. This is especially the case for immutable classes likeFraction.Fraction.__new__returns an object of its own super class and when that happens, python skips calling__init__completely. In the case ofFraction, its__init__is really justobject.__init__which only accepts the singleselfparameter and just returns without doing anything. It is never meant to be called.When you implemented
F1.__init__, you had a bug and this bug masks the problem. When you call a superclass method directly, you need to put theselfparameter in the call. You should have doneFraction.__init__(self, *args, **kwargs). Had you done so, you'd get the same error as in theF2case (becausesuper().__init__(*args, **kwargs)does add theself).But really you have a more pressing problem. Its okay to have your own
__init__, but with restrictions. You can't initialize theFractionbecause that was done in__new__before__init__was called. And you can't callFraction.__init__which does nothing except explode when given parameters. You could add other attributes to the object, but that's about it. But other strange things will happen. Unless you override methods like__add__, they will return objects of the originalFractiontype because that's the__new__that is being called. When your parent class uses__new__, you really want to override that__new__.