Python classes and __init__ method - python

I am learning python via dive into python. Got few questions and unable to understand, even through the documentation.
1) BaseClass
2) InheritClass
What exactly happens when we assign a InheritClass instance to a variable, when the InheritClass doesn't contain an __init__ method and BaseClass does ?
Is the BaseClass __init__ method called automatically
Also, tell me other things that happen under the hood.
Actually the fileInfo.py example is giving me serious headache, i am just unable to understand as to how the things are working. Following

Yes, BaseClass.__init__ will be called automatically. Same goes for any other methods defined in the parent class but not the child class. Observe:
>>> class Parent(object):
... def __init__(self):
... print 'Parent.__init__'
... def func(self, x):
... print x
...
>>> class Child(Parent):
... pass
...
>>> x = Child()
Parent.__init__
>>> x.func(1)
1
The child inherits its parent's methods. It can override them, but it doesn't have to.

#FogleBird has already answered your question, but I wanted to add something and can't comment on his post:
You may also want to look at the super function. It's a way to call a parent's method from inside a child. It's helpful when you want to extend a method, for example:
class ParentClass(object):
def __init__(self, x):
self.x = x
class ChildClass(ParentClass):
def __init__(self, x, y):
self.y = y
super(ChildClass, self).__init__(x)
This can of course encompass methods that are a lot more complicated, not the __init__ method or even a method by the same name!

Related

Inheritance: why does self refer to child class?

Sorry I could not come up with a better title.
Please consider this example:
class A:
def __init__(self, x):
self.attr = x
class B(A):
x = 1
def __init__(self):
super().__init__(x=self.x)
class C(B):
x = 2
def __init__(self):
super().__init__()
c = C()
print(c.attr) # 2
This code prints 2.
This means self in B.__init__ is an instance of C, not B.
I thought that the purpose of super was to refer to the methods/attributes of the parent class. Can somebody please explain the logic behind the output not being 1? Thank you.
The purpose of super() in super().__init__() is to call the __init__() method from the parent. It doesn't change the object itself -- it's still an instance of the subclass. So anything that the subclass overrides will be visible in the instance.
If the parent method doesn't want the overridden value of x, it should use B.x rather than self.x. The whole point of accessing the attribute through self is to take advantage of overriding in subclasses.

Invoking other class/object methods from inside the class

Suppose I have the following code:
class Classy:
def other(self):
print("other")
def method(self):
print("method")
self.other()
obj = Classy()
obj.method()
The output:
method
other
So I invoke another object/class method from inside the class. I invoke the other method within the 'method' method.
Now if I run the following code:
class Classy:
def other(self):
print("other")
def method(self):
print("method")
Classy.other(self)
obj = Classy()
obj.method()
The output is the same. Now my question is: What is the difference between these two?
I am not sure if it is just a different style of calling - so it is basically the same - or if there is a difference in the logic. If yes, I would be interested in an example where the difference matters.
Let's set it up so we can run them side by side:
class Classy:
def other(self):
print("Classy.other")
def method(self):
print("Classy.method")
self.other()
class NotClassy:
def other(self):
print("NotClassy.other")
def method(self):
print("NotClassy.method")
NotClassy.other(self)
So far, so good:
>>> Classy().method()
Classy.method
Classy.other
>>> NotClassy().method()
NotClassy.method
NotClassy.other
But what if inheritance gets involved, as it so often does in oop? Let's define two subclasses that inherit method but override other:
class ClassyToo(Classy):
def other(self):
print("ClassyToo.other")
class NotClassyToo(NotClassy):
def other(self):
print("NotClassyToo.other")
Then things get a bit problematic; although the subclasses have almost identical implementation, and the parent classes seemed to behave exactly the same, the outputs here are different:
>>> ClassyToo().method()
Classy.method
ClassyToo.other
>>> NotClassyToo().method()
NotClassy.method
NotClassy.other # what about NotClassyToo.other??
By calling NotClassy.other directly, rather than invoking the method on self, we've bypassed the overridden implementation in NotClassyToo. self might not always be an instance of the class the method is defined in, which is also why you see super getting used - your classes should cooperate in inheritance to ensure the right behaviour.

What is the difference between calling a method from parent class using super() and using self?

class Car:
def __init__(self, car_name, manufacturer, cost):
self.__car_name=car_name
self.__manufacturer=manufacturer
self.__cost=cost
def display_car_details(self):
print(self.__car_name, self.__manufacturer, self.__cost)
class Super_Car(Car):
def __init__(self, car_name, manufacturer, cost,
top_speed, material_used, engine_type):
super().__init__(car_name, manufacturer, cost)
self.__top_speed=top_speed
self.__material_used=material_used
self.__engine_type=engine_type
def display_super_car_details(self):
self.display_car_details() # this?
super().display_car_details() # or that?
print(self.__top_speed, self.__material_used,
self.__engine_type)
Please tell me the difference between calling display_car_details() by using self.… and calling by super().…. The method which calls the above function is in Super_car class with name display_super_car_details().
In your specific case, there is no difference. If the methods had the same name, you would not be calling the parent class using self.…, but your own method again, creating infinite recursion.
With super(), the call is always going to the method in the parent class in Method Resolution Order, thus preventing the infinite recursion. More details on super() itself can also be found in the official documentation.
Generally, you only need super() if you really need the inherited method, such as when re-implementing that exact method.
For example, running this code will lead to RecursionError, due to maximum recursion depth exceeded. The reason is that Bar.method1() calls itself over and over, because we are using self.… instead of super().….
class Foo:
def method1(self):
print("I just want to be called")
class Bar(Foo):
def method1(self):
self.method1() # oops
b = Bar()
b.method1()
This on the other hand has the intended effect:
class Foo:
def method1(self):
print("I just want to be called")
class Bar(Foo):
def method1(self):
super().method1() # sweet
b = Bar()
b.method1()
Again, in your specific example it does not matter, because the other method (display_car_details) is only defined in the parent class, so it is unambiguous which method to call. If you had overridden display_car_details with something different, the use of super() and self would again yield different results:
class Bar(Foo):
def method1(self):
print("You are not supposed to call this")
def method2(self):
super().method1()
def method3(self):
self.method1()
Observe the difference in the interactive interpreter:
>>> b = Bar()
>>> b.method1()
You are not supposed to call this
>>> b.method2()
I just want to be called
>>> b.method3()
You are not supposed to call this

How to call subclass methods in a superclass in Python

I want to know how to call subclass methods in the superclass.
I believe this is a pattern used often.
class A(object):
def x(self):
self.y()
def y(self):
print('default behavior')
class B(A):
def y(self):
print('Child B behavior')
class C(A):
def z(self):
pass
>>>B().x()
Child B behavior
>>>C().x()
default behavior
It is sort of like an abstract class, but provides default behavior. Can't remember the name of the pattern off the top of my head though.
The point behind a subclass is that it extends and alters the behaviour of the superclass. The superclass can't know how a subclass will extend it.
Edit: But it is well possible that the superclass knows, that the subclass will extend it. Not sure, if this is good design, though.
Here's what I've just tried:
class A(object):
def x(self):
print self.y()
class B(A):
def y(self):
return 1
>>> B().x()
1
So unless you had some specific problem, just call a method from the subclass in the base class and it should just work.
This is a very broad question. As you don't provide example code, I'll just show the easiest example:
class A(object):
def meth1(self, a):
self.meth2(a, a*a)
class B(A):
def meth2(self, a, b):
return b / a
b = B()
b.meth1(10)
I think this is related to using abstract methods. The parent class defines methods that the subclass should implement and the parent class knows that these methods will be implemented. Similar structures exist for example in Java.
Using these in Python is discussed here well: Abstract methods in Python

Only calling base class if its not subclassed in Python

I need to write a base class (in this example, class A) that will likely, but not always, be subclassed. I want to call the 'Run' method from the base class only if its not subclassed, else only call the the 'Run' method from the subclasses.
This is what I have, it seems to work but I'm wondering if there is an easier or more Pythonic way.
class A(object):
def __init__(self):
pass
def Run(self):
print "Calling A.Run()"
class B(A):
def __init__(self):
A.__init__(self)
pass
def Run(self):
print "Calling B.Run()"
subs = A.__subclasses__()
if subs: inst = [i() for i in subs]
else: inst = [A()]
[each.Run() for each in inst]
What you have looks correct, except that most programmers would look at the B.Run method and think: "Oh, he forgot to call super. Let me just add that..." but I'm sure you'd explain what you're doing in your real implementation :)
If you're worried about something like slicing in C++, then be reassured. What you have is good.
As for making it "easier", I'm not sure how you could simplify it aside from removing A's empty __init__ function and removing pass from B's __init__.
I want to call the 'Run' method from the base class only if its not subclassed, else only call the the 'Run' method from the subclasses.
This is what happens by default, the most specific method is called.
If you really don't want the base method to be available from subclasses, you can write something like this:
class C(object):
def Run(self):
if self.__class__ != C:
raise NotImplementedError("You need to override this method")

Categories

Resources