Python Programming

Lecture 10 Object-Oriented Programming

10.1 OOP Basics

  • 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(f"{self.name.title()} is now sitting.")

    def roll_over(self):
        print(f" {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(f"{self.name.title()} \
            is now sitting.")

    def roll_over(self):
        print(f"{self.name.title()} \
            rolled over!")

my_dog = Dog('willie', 6)

print(my_dog.name.title())
print(str(my_dog.age)+" years old")

Willie
6 years old

my_dog.sit()
my_dog.roll_over()

Willie is now sitting.
Willie rolled over!

your_dog = Dog('lucy', 3)
  • 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=f"{self.year} {self.make}\
                 {self.model}"
        return long_name.title()

    def read_odometer(self):
        print(f"This car has\
            {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(f"This car has\
            {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(f"This car has\
            {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(f"This car has\
            {self.odometer_reading}\
             miles on it.")

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

    def increment_odometer(self, miles):
        self.odometer_reading \
        = 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.

10.2 Inheritance, Polymorphism

  • 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=f"{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
Polymorphism
  • Defining attributes and methods for the child class


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=f"{self.year} {self.make}\
                 {self.model}"
        return long_name.title()

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

    def describe_battery(self):
        print(f"{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 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=f"{self.year} {self.make}\
                 {self.model}"
        return long_name.title()

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

    def describe_battery(self):
        print(f"{self.battery_size}-kWh battery.")

    def get_descriptive_name(self):
        long_name=f"{self.year} {self.make}"
        return long_name.title()
  • Open/closed principle

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

  • 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 
         
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

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 __init__(self,name,sex):
        Person.__init__(self,name,sex)
         
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
  • Overwrite completely


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

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

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

May female April  
  • Overwrite partially


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

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

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

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

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


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

10.3 Special Methods, Import Class

  • The precise definition of dir() is that it lists the methods and attributes of a Python object.


>>> stuff = list()
>>> dir(stuff)
['__add__', '__class__', '__contains__', '__delattr__',
'__delitem__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getitem__',
'__gt__', '__hash__', '__iadd__', '__imul__', '__init__',
'__iter__', '__le__', '__len__', '__lt__', '__mul__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__reversed__', '__rmul__', '__setattr__',
'__setitem__', '__sizeof__', '__str__', '__subclasshook__',
'append', 'clear', 'copy', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort']
  • Everything in Python is an object.


stuff = []
stuff.append('python')
stuff.append('chuck')
stuff.sort()

print(stuff[0])
print(stuff.__getitem__(0))
print(list.__getitem__(stuff,0))

chuck
chuck
chuck
  • Actually, the class you create inherits the "object" class.

  • Classes can intercept Python Operators


class Dog():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        return f"Dog's name is {self.name}."


my_dog = Dog('willie', 6)
print(dir(my_dog))

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
 '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', 
 '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
 '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 
'age', 'name']
  • __str__


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

my_dog = Dog('willie', 6)
print(my_dog)

<__main__.Dog object at 0x053A3750>

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

my_dog = Dog('willie', 6)
print(my_dog)

willie
  • __doc__, __name__, __module__, __bases__


class Dog():
    "This is about dog class."
    def __init__(self, name, age):
        self.name = name
        self.age = age

print(Dog.__doc__)
print(Dog.__name__)
print(Dog.__module__)
print(Dog.__bases__)

This is about dog class.
Dog
__main__
(<class 'object'>,)

class Dog():
    "This is about dog class."
    def __init__(self, name, age):
        self.name = name
        self.age = age
class Husky(Dog):
    pass

print(Husky.__bases__)

(<class '__main__.Dog'>,)
  • 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.

  • 
    from car import ElectricCar
    
    my_tesla = ElectricCar('tesla', 'model s', 2016)
    
    print(my_tesla.get_descriptive_name())
    
    
    2016 Tesla Model S
    
    
  • 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
    

Summary of OOP

  • Three Features of OOP

    Encapsulation, inheritance, polymorphism

  • Everything in Python is object.

  • Process-oriented programming (POP)

    Object-oriented Programming (OOP)

Summary

  • OOP
    • Reading: Python Crash Course, Chapter 9