Python Programming

Lecture 9 Object-Oriented Programming

9.1 Object-Oriented Programming

  • Object-oriented programming is one of the most effective approaches to writing software.

  • Python has been an object-oriented language since it existed. Because of this, creating and using classes and objects are downright easy.

  • Understanding object-oriented programming will help you see the world as a programmer does. It'll help you really know your code, not just what’s happening line by line, but also the bigger concepts behind it.

  • Creating and Using a Class


class Dog():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sit(self):
        print(self.name.title() + " is now sitting.")

    def roll_over(self):
        print(self.name.title() + " rolled over!")
  • By convention, capitalized names refer to classes in Python.

  • A function that's part of a class is a method.

  • The __init__() method is a special method Python runs automatically whenever we create a new instance based on the Dog class.

  • Variables that are accessible through instances like this are called attributes.

  • Making an Instance from a Class


class Dog():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sit(self):
        print(self.name.title() 
            + " is now sitting.")

    def roll_over(self):
        print(self.name.title() 
            + " rolled over!")
  • my_dog.name

  • my_dog.age

  • Creating Multiple Instances


class Dog():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sit(self):
        print(self.name.title() 
            + " is now sitting.")

    def roll_over(self):
        print(self.name.title() 
            + " rolled over!")
  • Working with Classes and Instances


class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        long_name = str(self.year) + ' ' + self.make + ' ' 
        + self.model
        return long_name.title()

    def read_odometer(self):
        print("This car has " + str(self.odometer_reading) 
            + " miles on it.")

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

2016 Audi A4
This car has 0 miles on it.
  • Modifying Attribute Values


class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def read_odometer(self):
        print("This car has " 
            + str(self.odometer_reading) 
            + " miles on it.")
  • Modifying an Attribute's Value Directly


my_new_car.odometer_reading = 23
my_new_car.read_odometer()

2016 Audi A4
This car has 23 miles on it.
  • Modifying Attribute Values


class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def read_odometer(self):
        print("This car has " 
            + str(self.odometer_reading) 
            + " miles on it.")

    def update_odometer(self, mileage):
        self.odometer_reading = mileage
  • Modifying an Attribute's Value Through a Method


my_new_car = Car('audi', 'a4', 2016)
my_new_car.update_odometer(23)
my_new_car.read_odometer()

This car has 23 miles on it.

my_new_car.update_odometer(30)
my_new_car.read_odometer()

This car has 30 miles on it.

class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def read_odometer(self):
        print("This car has " 
            + str(self.odometer_reading) 
            + " miles on it.")

    def update_odometer(self, mileage):
        self.odometer_reading = mileage

    def increment_odometer(self, miles):
        self.odometer_reading += miles
  • Modifying an Attribute's Value Through a Method


my_new_car = Car('audi', 'a4', 2016)
my_new_car.update_odometer(23)
my_new_car.read_odometer()

This car has 23 miles on it.

my_used_car.increment_odometer(100)
my_used_car.read_odometer()

This car has 123 miles on it.

9.2 Inheritance

  • You don't always have to start from scratch when writing a class. If the class you're writing is a specialized version of another class you wrote, you can use inheritance.

  • When one class inherits from another, it automatically takes on all the attributes and methods of the first class.

  • The original class is called the parent class, and the new class is the child class. The child class inherits every attribute and method from its parent class but is also free to define new attributes and methods of its own.


class Car():
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
    def get_descriptive_name(self):
        long_name = str(self.year) + ' ' 
        + self.make + ' ' + self.model
        return long_name.title()

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())

2016 Tesla Model S
  • Defining attributes and methods for the child class


class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery_size = 70
    def describe_battery(self):
        print(str(self.battery_size)+"-kWh battery.")

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

2016 Tesla Model S
70-kWh battery.
  • Overriding methods from the parent class


class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery_size = 70

    def describe_battery(self):
        print("This car has a "+str(self.battery_size)+"-kWh battery.")

    def get_descriptive_name(self):
        long_name = str(self.year) + ' ' 
        + self.make
        return long_name.title()
  • Now if someone tries to call get_descriptive_name() with an electric car, Python will ignore the method get_descriptive_name() in Car and run this code instead.

  • Instances as attributes


class Battery():
    def __init__(self, battery_size=70):
        self.battery_size = battery_size
    def describe_battery(self):
        print("This car has a "+str(self.battery_size)+"-kWh battery.")

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar('tesla', 'model s', 2016)
my_tesla.battery.describe_battery()

This car has a 70-kWh battery.

9.3 Importing Classes

  • Importing a Single Class

  • Save your Car class in a python file: car.py


from car import Car

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

2016 Audi A4
This car has 23 miles on it.
  • Storing Multiple Classes in a Module

  • You can store as many classes as you need in a single module, although each class in a module should be related somehow. The classes Battery and ElectricCar both help represent cars, so let's add them to the module car.py


from car import ElectricCar

my_tesla = ElectricCar('tesla', 'model s', 2016)

print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.
  • Importing Multiple Classes from a Module


from car import Car, ElectricCar

my_beetle = Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())

my_tesla = ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())

2016 Volkswagen Beetle
2016 Tesla Roadster
  • Importing a Module into a Module

  • car.py

  • 
    class Car():
    ...
    
  • electric_car.py

  • 
    from car import Car
    
    class Battery():
    ...
    
    class ElectricCar(Car):
    ...
    
  • my_cars.py


from car import Car
from electric_car import ElectricCar
  • Using code two ways


class Person:
    def __init__(self, name, job=None, pay=0):
        self.name = name
        self.job = job
        self.pay = pay
#test
bob = Person('Bob Smith') 
sue = Person('Sue Jones', job='dev', pay=100000) 
print(bob.name, bob.pay) 
print(sue.name, sue.pay) 

class Person:
    def __init__(self, name, job=None, pay=0):
        self.name = name
        self.job = job
        self.pay = pay

if __name__ == '__main__': 
    # self-test code
    bob = Person('Bob Smith')
    sue = Person('Sue Jones', job='dev', pay=100000)
    print(bob.name, bob.pay)
    print(sue.name, sue.pay)

9.4 3 ways of inheritance

  • We have learned a way to inherit, i.e., super(). There are two other ways.

class Person:
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
        
    def print_title(self):
        if self.sex == "male":
            print("man")
        elif self.sex == "female":
            print("woman")

class Child(Person):                            
    pass #It does not do anything, but avoid error.

if __name__ == '__main__':             
    May = Child("May","female")
    Peter = Person("Peter","male")

    print(May.name,May.sex,Peter.name,Peter.sex)    
    May.print_title()
    Peter.print_title()

May female Peter male
woman
man
  • Check


class Person:
    pass

class Child(Person):                 
    pass

May = Child()
Peter = Person()    

print(isinstance(May,Child))         # True
print(isinstance(May,Person))        # True
print(isinstance(Peter,Child))       # False
print(isinstance(Peter,Person))      # True
print(issubclass(Child,Person))      # True
  • polymorphism


class Person:
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
        
    def print_title(self):
        if self.sex == "male":
            print("man")
        elif self.sex == "female":
            print("woman")

class Child(Person):              
    def print_title(self):
        if self.sex == "male":
            print("boy")
        elif self.sex == "female":
            print("girl")

if __name__ == '__main__':        
    May = Child("May","female")
    Peter = Person("Peter","male")

    print(May.name,May.sex,Peter.name,Peter.sex)
    May.print_title()
    Peter.print_title()

May female Peter male
girl
man
  • Open/closed principle

  • Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

  • Class Constructor


class Person:
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex

class Child(Person):                
    def __init__(self,name,sex,mother,father):
        self.name = name
        self.sex = sex
        self.mother = mother
        self.father = father

May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)    

May female April June   

class Person:
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex

class Child(Person):                          
    def __init__(self,name,sex,mother,father):
        Person.__init__(self,name,sex)    
        self.mother = mother
        self.father = father

May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)

May female April June

Super(), Common Inheritance, and w/o __init__


class Person:
    def __init__(self,name):
        self.name = name        
class Child(Person):                            
    pass #w/o __init__
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

May Peter

class Person:
    def __init__(self,name):
        self.name = name
        print(self.name) #add a print here        
class Child(Person):                            
    pass #w/o __init__
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

May
Peter
May Peter

class Person:
    def __init__(self,name):
        self.name = name
        print(self.name) #add a print here       
class Child(Person): 
    def __init__(self,name):                           
        self.name = name 
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

Peter
May Peter

class Person:
    def __init__(self,name):
        self.name = name
        print(self.name) #add a print here        
class Child(Person): 
    def __init__(self,name):                           
        self.name = name 
        print(self.name)
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

May
Peter
May Peter

class Person:
    def __init__(self,name):
        self.name = name
        print(self.name) #add a print here        
class Child(Person): 
    def __init__(self,name):                           
        super().__init__(name) 
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

May
Peter
May Peter

class Person:
    def __init__(self,name):
        self.name = name
        print(self.name) #add a print here       
class Child(Person): 
    def __init__(self,name):                           
        Person.__init__(self, name)
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

May
Peter
May Peter

class Person:
    def __init__(self,name):
        self.name = name
        print(self.name) #add a print here       
class Child(Person): 
    def __init__(self,name):                           
        super().__init__(name) 
        #or Person.__init__(self,name)
        print(self.name)
if __name__ == '__main__':             
    May = Child("May")
    Peter = Person("Peter")
    print(May.name,Peter.name) 

May
May
Peter
May Peter
  • Person. and super(). are almost the same for the single inheritance with or without modification. Do not mix them together!

  • If you just need complete inheritance of attributes, then all three ways are equivalent.

  • Without Person. super(), __init__ in the child class overwrites the __init__ in the parent class.

  • You would better use Person. or super() to inherit or extend the method of the parent class.

  • There is no so-called, "new-style-and-classic-classes" (新式类,经典类) in Python 3.x.

Summary

  • OOP
    • Reading: Python Crash Course, Chapter 9