Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a derived class automatically have all the attributes of the base class?

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?

like image 938
liamel1i Avatar asked Jun 18 '11 13:06

liamel1i


People also ask

What characteristics does a derived class have with its base class?

The derived class inherits all members and member functions of a base class. The derived class can have more functionality with respect to the Base class and can easily access the Base class. A Derived class is also called a child class or subclass.

Can a class be both a base class and a derived class?

A derived class can have only one direct base class.

What does derived class does not inherit from the base class?

Following are the properties which a derived class doesn't inherit from its parent class : 1) The base class's constructors and destructor. 2) The base class's friend functions. 3) Overloaded operators of the base class.

How is a derived class different from its base class?

A base class is an existing class from which the other classes are derived and inherit the methods and properties. A derived class is a class that is constructed from a base class or an existing class.


2 Answers

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.

like image 95
Henry Gomersall Avatar answered Oct 18 '22 03:10

Henry Gomersall


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.

like image 31
Cat Plus Plus Avatar answered Oct 18 '22 04:10

Cat Plus Plus