I am learning python classes and could not understand below behavior:
In below example, I am extending built-in str
class:
class uStr(str):
def __init__(self,u_str):
str.__init__(u_str) #<<<------- no self is needed
def underline(self):
underlines = format('', '-^' + str(len(self)))
return str.__str__(self) + '\n' + underlines
In below example, I am extending a user-defined class Fraction
:
from fraction import *
class MixedFraction(Fraction):
def __init__(self, *args):
Fraction.__init__(self, args[0], args[1]) #<<<-------self is needed
def getWholeNum(self):
return self.getNumerator() // self.getDenominator()
Why do we need to give self
as argument in 2nd example in calling __init__
of super class, while there is no need to give self
in calling str
's __init__
.
First, you don't need those __init__
implementations at all. You could just inherit the superclass implementations. If you actually need to customize construction, then for uStr
, you should do it in __new__
instead of __init__
.
str
doesn't have its own __init__
. It does all its initialization in __new__
, because str
instances are immutable and initializing in __init__
would be mutative. str
inherits __init__
from object
.
object.__init__
and object.__new__
are a little weird. If exactly one of object.__init__
or object.__new__
are overridden, then the overridden one throws an error if given arguments (beyond self
) and the other ignores arguments, to save you the work of having to provide a do-nothing override. However, if both or neither are overridden, then both will throw an error if given arguments (beyond self
). You can see this explained in a big comment in the source code.
str
implements __new__
, but inherits __init__
from object
. When you override __init__
and call str.__init__
, you're really calling object.__init__
.
When you call str.__init__(u_str)
, you're actually making an object.__init__
call for u_str
, the wrong object, rather than for self
. Since u_str
doesn't have an __init__
override (and since you're only passing one argument, which gets interpreted as self
), object.__init__
silently does nothing.
When you call str.__init__(self, u_str)
, you're making an object.__init__
call for self
, but since self
has both __new__
and __init__
overridden, object.__init__
complains about the u_str
argument.
It doesn't look like you actually need to override construction at all in your subclasses. If you do, the correct way to customize construction of a str
subclass is to override __new__
. If for some strange reason you had to call str.__init__
, the correct call would be str.__init__(self)
or super().__init__()
. Since object.__init__
doesn't do any initialization, you could also leave out the superclass constructor call.