There seems to be no good online documentation on this:
If I make a derived class, will it automatically have all the attributes of the base class? But what's the BaseClass.__init()
for, do you also need to do it to other base class methods? Does BaseClass.__init__()
need arguments? If you have arguments for your base class __init__()
, are they also used by the derived class, do you need to explicitly set the arguments to the derived classe's __init__()
, or set them to BaseClass.__init__()
instead?
If you implement __init__
in a class derived from BaseClass, then it will overwrite the inherited __init__
method and so BaseClass.__init__
will never be called. If you need to call the __init__
method for BaseClass (as is normally the case), then its up to you to do that, and its done explicitly by calling BaseClass.__init__
, normally from within the newly implemented __init__
method.
class Foo(object):
def __init__(self):
self.a = 10
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
self.b = 20
bar = Bar()
bar.do_something()
This will cause the following error:
AttributeError: 'Bar' object has no attribute 'a'
So, the do_something
method has been inherited as expected, but that method requires the attribute a
to have been set, which it never is because __init__
was also overwritten. We get round this by explicitly calling Foo.__init__
from within Bar.__init__
.
class Foo(object):
def __init__(self):
self.a = 10
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
Foo.__init__(self)
self.b = 20
bar = Bar()
bar.do_something()
which prints 10
as expected. Foo.__init__
in this case expects a single argument which is an instance of Foo
(which by convention is called self
).
Normally, when you call a method on an instance of a class, the class instance is passed automatically as the first argument. Methods on an instance of a class are called bound methods. bar.do_something
is an example of a bound method (and you'll note that it is called without any arguments). Foo.__init__
is an unbound method because it is not attached to a particular instance of Foo
, so the first argument, an instance of Foo
, needs to be passed explicitly.
In our case, we pass self
to Foo.__init__
, which is the instance of Bar
that was passed to the __init__
method in Bar
. Since Bar
inherits from Foo
, instances of Bar
are also instances of Foo
, so passing self
to Foo.__init__
is allowed.
It is likely be the case that the class you are inheriting from requires or accepts more arguments than just an instance of the class. These are dealt with as you would with any method you're calling from within __init__
:
class Foo(object):
def __init__(self, a=10):
self.a = a
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
Foo.__init__(self, 20)
bar = Bar()
bar.do_something()
which would print 20
.
If you're trying to implement a interface that fully exposes all the initialisation arguments of the base class through your inheriting class, you'll need to do so explicitly. This is typically done with the *args and **kwargs arguments (the names are by convention), which are placeholders for all rest of the arguments that aren't explicitly named. The following example makes use of everything I've discussed:
class Foo(object):
def __init__(self, a, b=10):
self.num = a * b
def do_something(self):
print self.num
class Bar(Foo):
def __init__(self, c=20, *args, **kwargs):
Foo.__init__(self, *args, **kwargs)
self.c = c
def do_something(self):
Foo.do_something(self)
print self.c
bar = Bar(40, a=15)
bar.do_something()
In this case, the argument c
is set to be 40, as it's the first argument to Bar.__init__
. The second argument is then incorporated into the variables args
and kwargs
(the * and ** is specific syntax that says expand the list/tuple or dictionary into separate arguments when passing to a function/method), and is passed on to Foo.__init__
.
This example also makes the point that any overwritten method needs to be called explicitly if that is what is required (as do_something
is in this case).
One final point, you will often see super(ChildClass, self).method()
(where ChildClass
is some arbitrary child class) being used instead of a call to the BaseClass
method explicitly. Discussion of super
is a whole other question, but suffice it to say, in these cases it's typically being used to do exactly what is being done by calling BaseClass.method(self)
. Briefly, super
delegates the method call to the next class in the method resolution order - the MRO (which in single inheritance is the parent class). See the documentation on super for more info.
Bar
extends Foo
with the c
attribute. Foo
can't take a keyword c
. Unless I'm missing something? –
Acutance If I make a derived class, will it automatically have all the attributes of the base class?
Class attributes, yes. Instance attributes, no (simply because they don't exist when the class is created), unless there's no __init__
in the derived class, in which case the base one will be called instead, and will set the instance attributes.
Does BaseClass.init() need arguments?
Depends on the class and its __init__
signature. If you're explicitly calling Base.__init__
in the derived class, you will at least need to pass self
as the first argument. If you have
class Base(object):
def __init__(self):
# something
then it's rather obvious that no other arguments are accepted by the __init__
. If you'd have
class Base(object):
def __init__(self, argument):
# something
then you have to pass argument
when calling base __init__
. No rocket science in here.
If you have arguments for your base class init(), are they also used by the derived class, do you need to explicitly set the arguments to the derived classe's init(), or set them to BaseClass.init() instead?
Again, if the derived class doesn't have __init__
, base one will be used instead.
class Base(object):
def __init__(self, foo):
print 'Base'
class Derived(Base):
pass
Derived() # TypeError
Derived(42) # prints Base
In other case, you need to take care of it somehow. Whether you use *args, **kwargs
and just pass arguments unmodified to the base class, or copy the base class signature, or supply arguments from elsewhere, depends on what you're trying to accomplish.
© 2022 - 2024 — McMap. All rights reserved.