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
CodePudding user response:
TLDR; Fraction
uses __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 default object.__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 like Fraction
. Fraction.__new__
returns an object of its own super class and when that happens, python skips calling __init__
completely. In the case of Fraction
, its __init__
is really just object.__init__
which only accepts the single self
parameter 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 the self
parameter in the call. You should have done Fraction.__init__(self, *args, **kwargs)
. Had you done so, you'd get the same error as in the F2
case (because super().__init__(*args, **kwargs)
does add the self
).
But really you have a more pressing problem. Its okay to have your own __init__
, but with restrictions. You can't initialize the Fraction
because that was done in __new__
before __init__
was called. And you can't call Fraction.__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 original Fraction
type because that's the __new__
that is being called. When your parent class uses __new__
, you really want to override that __new__
.