\documentclass[14pt,compress,aspectratio=169]{beamer} \input{macros.tex} \title[OOP Inheritance]{Advanced Python} \subtitle{Object Oriented Programming: Inheritance} \author[FOSSEE] {The FOSSEE Group} \institute[IIT Bombay] {Department of Aerospace Engineering\\IIT Bombay} \date[] {Mumbai, India} \begin{document} \begin{frame} \titlepage \end{frame} \begin{frame} \frametitle{Recap} \begin{itemize} \item Created a new \lstinline{Talk} class \item Puts together data and methods \item Can make instances of the class \item Each instance has its own data \item Objects encapsulate data and behavior \end{itemize} \end{frame} \begin{frame}[fragile, plain] \frametitle{Recap: the Talk class} \vspace*{-0.1in} \begin{lstlisting} class Talk: """A class for the Talks.""" def __init__(self, speaker, title, tags): self.speaker = speaker self.title = title self.tags = tags def get_speaker_firstname(self): return self.speaker.split()[0] def get_tags(self): return self.tags.split(',') \end{lstlisting} \end{frame} \begin{frame}[fragile] \frametitle{Instantiating a class to create objects} \begin{lstlisting} In []: bdfl = Talk('Guido van Rossum', ...: 'The History of Python', ...: 'python,history,C,advanced') In []: bdfl.get_tags() In []: bdfl.get_speaker_firstname() In []: bdfl.tags In []: type(bdfl) \end{lstlisting} \end{frame} \begin{frame}[fragile] \frametitle{Classes: the big picture} \begin{itemize} \item Lets you create new data types \item Class is a template for an object belonging to that class \item Instantiating a class creates an instance (an object) \item An instance encapsulates the state (data) and behavior (methods) \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Objects and Methods} \begin{itemize} \item Objects group data with functions \item Everything in Python is an object \item Strings, lists, functions and even modules \end{itemize} \begin{lstlisting} s = "Hello World" s.lower() l = [1, 2, 3, 4, 5] l.append(6) \end{lstlisting} \end{frame} \begin{frame}[fragile] \frametitle{Classes} \begin{lstlisting} In []: s = "Hello World" In []: type(s) \end{lstlisting} \begin{itemize} \item A new string, comes along with methods \item A template or a blue-print, where these definitions lie \item This blue print for building objects is called a \lstinline{class} \item \lstinline{s} is an object of the \lstinline{str} class \item An object is an ``instance'' of a class \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Classes: inheritance} \begin{itemize} \item Allows you to define an inheritance hierarchy \begin{itemize} \item ``A Honda car \alert{is a} car.'' \item ``A car \alert{is an} automobile.'' \item ``A Python \alert{is a} reptile.'' \end{itemize} \item All data/behavior of a car should be there in a ``Honda'' car \item All data/behavior associated with a reptile ought to inhere in a snake \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Inheritance} \vspace*{-0.1in} \begin{itemize} \item Suppose, we wish to write a \lstinline{Tutorial} class \item It's almost same as \lstinline{Talk} except for minor differences \item We can ``inherit'' from \lstinline{Talk} \end{itemize} \pause \begin{lstlisting} class Tutorial(Talk): """A class for the tutorials.""" def __init__(self, speaker, title, tags, needs_computer=True): super().__init__(speaker, title, tags) self.needs_computer = needs_computer \end{lstlisting} \end{frame} \begin{frame}[fragile] \frametitle{Inheritance} \vspace*{-0.1in} \begin{itemize} \item Modified \lstinline{__init__} method \item Inherits: \lstinline{get_tags} and \lstinline{get_speaker_firstname} \end{itemize} \begin{lstlisting} tut = Tutorial('Travis Oliphant', 'Numpy Basics', 'numpy,python,beginner') tut.get_speaker_firstname() tut.needs_computer \end{lstlisting} \end{frame} \begin{frame} \frametitle{Some points} \begin{itemize} \item \lstinline{Tutorial} is a subclass (or child) of \lstinline{Talk} \item \lstinline{Tutorial} is derived from (inherits from) \lstinline{Talk} \item \lstinline{Talk} is the base class (or parent class) \item Only the \lstinline{__init__} has been changed \item This is called overriding \item \lstinline{super} ensures that the parent class is called \item Calling the parent is not automatic \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Exercise: try this} \begin{lstlisting} class Tutorial(Talk): def __init__(self, speaker, title, tags, needs_computer=True): self.needs_computer = needs_computer In []: t = Tutorial('G v R', 'Python', 'py,tut') In []: t.needs_computer In []: t.speaker In []: t.get_speaker_firstname() \end{lstlisting} \end{frame} \begin{frame} \frametitle{Inheritance} \begin{itemize} \item A \lstinline{Tutorial} is a \lstinline{Talk} \item A \lstinline{Talk} is not a \lstinline{Tutorial} \item The sentence above should make sense! \item A \lstinline{Cat} is an \lstinline{Animal} \end{itemize} \end{frame} \begin{frame} \frametitle{Incorrect inheritance} \begin{itemize} \item A \lstinline{Talk} is a \lstinline{Speaker} or \item A \lstinline{Speaker} is a \lstinline{Room} \item Something is wrong in your OO design! \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Summary} \begin{itemize} \item Inheritance \item The importance of making sense \item The \lstinline{super} function \end{itemize} \end{frame} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Exercises \begin{frame}[fragile] \frametitle{Exercise: \lstinline{Animal} class} \begin{block}{} Create an \lstinline{Animal} class that has a name attribute (str) and a single method called \lstinline{greet} which takes no arguments but returns a string about how the animal makes a greeting. By default let \lstinline{greet} return \lstinline{' says greet'}. \end{block} \begin{lstlisting} In []: a = Animal('human') In []: a.greet() Out[]: 'human says greet' \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{lstlisting} class Animal: def __init__(self, name): self.name = name def greet(self): return self.name + ' says greet' \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{itemize} \item Note that this is the same as \end{itemize} \begin{lstlisting} class Animal(object): # ... \end{lstlisting} \begin{itemize} \item Otherwise \lstinline{object} is implicitly the base class \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Exercise: \lstinline{Cat} class} \begin{block}{} Create a \lstinline{Cat} class which derives from the \lstinline{Animal} class \alert{override} \lstinline{greet} return \lstinline{' says meow'}. \end{block} \begin{lstlisting} In []: a = Cat('Felix') In []: a.greet() Out[]: 'Felix says meow' \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{lstlisting} class Animal: def __init__(self, name): self.name = name def greet(self): return self.name + ' says greet' class Cat(Animal): def greet(self): return self.name + ' says meow' \end{lstlisting} \begin{itemize} \item Note, no need to override \lstinline{__init__} \end{itemize} \end{frame} \begin{frame}[plain, fragile] \frametitle{Observation: order matters} \begin{lstlisting} class Cat(Animal): def greet(self): return self.name + ' says meow' class Animal: def __init__(self, name): self.name = name def greet(self): return self.name + ' says greet' \end{lstlisting} \begin{itemize} \item Will the above code work? Explore this and see what error you get. \end{itemize} \end{frame} \begin{frame}[plain, fragile] \frametitle{Exercise: \lstinline{Mammal} class} \begin{block}{} Create a \lstinline{Mammal} class that derives from \lstinline{Animal} that takes an additional argument specifying the number of legs (an integer) and stores it in the attribute \lstinline{legs}. \end{block} \begin{lstlisting} In []: m = Mammal('dog', 4) In []: m.legs Out[]: 4 In []: m.greet() Out[]: 'dog says greet' \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{lstlisting} class Mammal(Animal): def __init__(self, name, legs): super().__init__(name) self.legs = legs \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution alternative} \begin{lstlisting} class Mammal(Animal): def __init__(self, name, legs): self.name = name self.legs = legs \end{lstlisting} \begin{itemize} \item Will also work but a \alert{bad} idea \item What if \lstinline{Animal.__init__} changes? \end{itemize} \end{frame} \begin{frame}[plain, fragile] \frametitle{More on \typ{super}} We can also do: \begin{lstlisting} class Mammal(Animal): def __init__(self, name, legs): super(Mammal, self).__init__(name) In []: super? In []: m = Mammal('dog', 4) In []: super(Mammal, m) In []: super(Mammal, m).greet() In []: super(Animal, m).greet() # ?? \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Exercise: \lstinline{Human} class} \begin{block}{} Create a \lstinline{Human} class that derives from \lstinline{Mammal} modify its \lstinline{greet} method to return \lstinline{' says hello'}. Also change the default number of legs to 2. \end{block} \begin{lstlisting} In []: h = Human('Ram') In []: h.legs Out[]: 2 In []: h.greet() Out[]: 'Ram says hello' \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{lstlisting} class Human(Mammal): def __init__(self, name, legs=2): super().__init__(name, legs) def greet(self): return self.name + ' says hello' \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Complete Solution} \vspace*{-0.1in} \small \begin{lstlisting} class Animal: def __init__(self, name): self.name = name def greet(self): return self.name + ' says greet' class Mammal(Animal): def __init__(self, name, legs): super().__init__(name) self.legs = legs class Human(Mammal): def __init__(self, name, legs=2): super().__init__(name, legs) def greet(self): return self.name + ' says hello' \end{lstlisting} \end{frame} \begin{frame} \frametitle{Observations} \begin{itemize} \item \py{Human} is a \py{Mammal} \item \py{Human} is an \py{Animal} \end{itemize} \end{frame} \begin{frame}[plain, fragile] \frametitle{Exercise: Methods in subclass} \begin{block}{} Add a method to \py{Human} called \py{speak} which prints \py{'My name is '}. \end{block} \begin{lstlisting} In []: h = Human('Ram') In []: h.speak() My name is Ram \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{lstlisting} class Human(Mammal): def __init__(self, name, legs=2): super().__init__(name, legs) def greet(self): return self.name + ' says hello' def speak(self): print('My name is', self.name) \end{lstlisting} \end{frame} \begin{frame} \frametitle{Observations} \begin{itemize} \item \py{Human} is a \py{Mammal} \item \py{Human} is an \py{Animal} \item \py{Human} can speak but \py{Mammal, Animal} cannot \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Exercise: class in a module} \begin{block}{} Put the \py{Animal, Mammal, Human} classes inside a Python file, called \texttt{oop.py} somewhere convenient in your computer. Add the following code at the bottom. \end{block} \begin{lstlisting} print(__name__) h = Human('Sam') print(h.greet()) print(h.legs) print(type(h)) \end{lstlisting} \begin{itemize} \item Execute this module using \texttt{python oop.py} \item Import the \py{Human} class from IPython \end{itemize} \end{frame} \begin{frame}[plain, fragile] \frametitle{Exercise: overriding a \typ{list} method} \begin{block}{} Create a subclass of the standard Python \py{list} called \py{MyList} and override just the \py{append} method to print the item appended as \py{'appending '} and then call the parent append so as to internally append the item. The usage should be as follows: \end{block} \begin{lstlisting} In []: l = MyList() In []: l.append(1) appending 1 In []: l Out[]: [1] \end{lstlisting} \end{frame} \begin{frame}[plain, fragile] \frametitle{Solution} \begin{lstlisting} class MyList(list): def append(self, item): print('appending', item) super().append(item) \end{lstlisting} \end{frame} \end{document}