HyperLearning AI - Introduction to Python

An introductory course to the Python 3 programming language, with a curriculum aligned to the Certified Associate in Python Programming (PCAP) examination syllabus (PCAP-31-02).
https://knowledgebase.hyperlearning.ai/courses/introduction-to-python

08. Classes and Objects Part 1

https://knowledgebase.hyperlearning.ai/courses/introduction-to-python/modules/8/classes-and-objects-part-1

In this module we will introduce the object oriented programming (OOP) paradigm - a means to model the world and our software applications as objects that interact with each other. Supported by hands-on examples in Python, we will explore the fundamental concepts in object oriented programming, including:

  • Classes - classes, superclasses, subclasses, inheritance, and creating objects
  • Class Attributes - class variables, instance variables, managing attributes and explicit constructor invocation
  • Class Methods - defining and using class methods, the self parameter, the init method and the str method
  • Inheritance - inheritance, overriding, single inheritance and multiple inheritance
  • Constructors - writing and using constructors
  • Introspection - dict, name, module and bases properties, and examining class structure

1. Programming Paradigms

1.1. Imperative Programming

In [3]:
# Write a program using imperative programming to calculate the sum of a given list of numbers
sum = 0
my_numbers = [1, 2, 3, 4, 5]
for number in my_numbers:
    sum += number
print(f'The sum of the numbers in {my_numbers} is: {sum}')
The sum of the numbers in [1, 2, 3, 4, 5] is: 15

1.2. Functional Programming

In [5]:
from functools import reduce

# Write a program using functional programming to calculate the sum of a given list of numbers
def add(x, y):
    return x + y
sum = reduce(add, my_numbers)
print(sum)

# Write a program using functional programming and a lambda function to calculate the sum of a given list of numbers
sum = reduce(lambda x, y: x + y, my_numbers)
print(sum)
15
15

2. OOP Fundamentals

2.2.1. Creating Objects

In [16]:
# Update sys.path so that it can find our example module
import sys
sys.path.append('examples/formulapy')
In [17]:
# Import our example module containing our Car class definition
from example import Car
In [18]:
# Try to create a new car object without the required arguments
my_car = Car()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-0baf2074a667> in <module>
      1 # Try to create a new car object without the required arguments
----> 2 my_car = Car()

TypeError: __init__() missing 8 required positional arguments: 'number_doors', 'registration_number', 'make', 'model', 'year_manufactured', 'maximum_speed', 'acceleration_rate', and 'deceleration_rate'
In [19]:
# Create a new car object
mercedes_f1 = Car(number_doors = 0,
                  registration_number = 'MERC 123',
                  make = 'Mercedes',
                  model = 'AMG F1 W10 EQ Power+',
                  year_manufactured = 2019,
                  maximum_speed = 200,
                  acceleration_rate = 20,
                  deceleration_rate = 50)

# Print the type of object that this is i.e. the class that was used to instantiate this object
print(type(mercedes_f1))

# Print a string representation of the car object
print(mercedes_f1)

# Create another new car object
ferrari_f1 = Car(number_doors = 0,
                 registration_number = 'MON 888',
                 make = 'Ferrari',
                 model = 'SF1000',
                 year_manufactured = 2020,
                 maximum_speed = 200,
                 acceleration_rate = 15,
                 deceleration_rate = 60)

# Print a string representation of the car object
print(ferrari_f1)
<class 'example.Car'>
Car{'number_doors': 0, 'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 200, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0}
Car{'number_doors': 0, 'registration_number': 'MON 888', 'make': 'Ferrari', 'model': 'SF1000', 'year_manufactured': 2020, 'maximum_speed': 200, 'acceleration_rate': 15, 'deceleration_rate': 60, 'mileage_miles': 0, 'speed_mph': 0}

2.2.2. Accessing Attributes

In [5]:
# Access and display the maximum speed of the Mercedes car object
print(mercedes_f1.maximum_speed)

# Access and display the registration number of the Ferrari car object
print(ferrari_f1.registration_number)
200
MON 888

2.2.3. Modifying Attributes

In [6]:
# Modify the maximum speed of the Mercedes car object
mercedes_f1.maximum_speed = 220
print(mercedes_f1)

# Modify the registration number of the Ferrari car object
ferrari_f1.registration_number = 'SCUD 888'
print(ferrari_f1)
Car{'number_doors': 0, 'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 220, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0}
Car{'number_doors': 0, 'registration_number': 'SCUD 888', 'make': 'Ferrari', 'model': 'SF1000', 'year_manufactured': 2020, 'maximum_speed': 200, 'acceleration_rate': 15, 'deceleration_rate': 60, 'mileage_miles': 0, 'speed_mph': 0}

2.2.4. Deleting Attributes

In [7]:
# Delete the number of doors attribute belonging to the Mercedes car object
print(mercedes_f1)
print(mercedes_f1.number_doors)
del mercedes_f1.number_doors
print(mercedes_f1)
print(mercedes_f1.number_doors)
Car{'number_doors': 0, 'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 220, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0}
0
Car{'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 220, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0}
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-7-1a9fbe7cf1f9> in <module>
      4 del mercedes_f1.number_doors
      5 print(mercedes_f1)
----> 6 print(mercedes_f1.number_doors)

AttributeError: 'Car' object has no attribute 'number_doors'

2.2.5. Deleting Objects

In [8]:
# Create a new car object
redbull_f1 = Car(number_doors = 0,
                 registration_number = 'RB 999',
                 make = 'Red Bull',
                 model = 'RB9',
                 year_manufactured = 2013,
                 maximum_speed = 210,
                 acceleration_rate = 18,
                 deceleration_rate = 60)

# Print a string representation of the car object
print(redbull_f1)
Car{'number_doors': 0, 'registration_number': 'RB 999', 'make': 'Red Bull', 'model': 'RB9', 'year_manufactured': 2013, 'maximum_speed': 210, 'acceleration_rate': 18, 'deceleration_rate': 60, 'mileage_miles': 0, 'speed_mph': 0}
In [9]:
# Delete the previously created car object
del redbull_f1

# Try to print a string representation of the deleted car object
print(redbull_f1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-9-76a3d273f9ff> in <module>
      3 
      4 # Try to print a string representation of the deleted car object
----> 5 print(redbull_f1)

NameError: name 'redbull_f1' is not defined

2.2.6. Introspection

In [10]:
# Access an object's attribute references
print(mercedes_f1.__dict__)
{'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 220, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0}
In [11]:
# Access a class's name
print(Car.__name__)

# Access an object's class name (or type name) from which it was instantiated
print(mercedes_f1.__class__)
print(mercedes_f1.__class__.__name__)
Car
<class 'example.Car'>
Car
In [12]:
# Access the name of the module that a class was defined in
print(Car.__module__)
print(mercedes_f1.__class__.__module__)
example
example
In [13]:
# Access a class's base classes
print(Car.__bases__)
print(mercedes_f1.__class__.__bases__)
(<class 'object'>,)
(<class 'object'>,)

2.2.7. Adding Attributes

In [14]:
# Add a completely new attribute to one of our car objects that was not defined in the Car class definition
print(mercedes_f1)
setattr(mercedes_f1, 'height_mm', 950)
setattr(mercedes_f1, 'width_mm', 2000)
setattr(mercedes_f1, 'weight_kg', 743)
setattr(mercedes_f1, 'power_kw', 750)
print(mercedes_f1)
Car{'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 220, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0}
Car{'registration_number': 'MERC 123', 'make': 'Mercedes', 'model': 'AMG F1 W10 EQ Power+', 'year_manufactured': 2019, 'maximum_speed': 220, 'acceleration_rate': 20, 'deceleration_rate': 50, 'mileage_miles': 0, 'speed_mph': 0, 'height_mm': 950, 'width_mm': 2000, 'weight_kg': 743, 'power_kw': 750}

2.2.8. Invoking Methods

In [15]:
# Invoke the Car.accelerate() method on an existing car object
ferrari_f1.accelerate()
23/08/2020 21:01:21: Ferrari SF1000 current speed: 0mph
23/08/2020 21:01:22: Ferrari SF1000 current speed: 15mph
23/08/2020 21:01:23: Ferrari SF1000 current speed: 30mph
23/08/2020 21:01:24: Ferrari SF1000 current speed: 45mph
23/08/2020 21:01:25: Ferrari SF1000 current speed: 60mph
23/08/2020 21:01:26: Ferrari SF1000 current speed: 75mph
23/08/2020 21:01:27: Ferrari SF1000 current speed: 90mph
23/08/2020 21:01:28: Ferrari SF1000 current speed: 105mph
23/08/2020 21:01:29: Ferrari SF1000 current speed: 120mph
23/08/2020 21:01:30: Ferrari SF1000 current speed: 135mph
23/08/2020 21:01:31: Ferrari SF1000 current speed: 150mph
23/08/2020 21:01:32: Ferrari SF1000 current speed: 165mph
23/08/2020 21:01:33: Ferrari SF1000 current speed: 180mph
23/08/2020 21:01:34: Ferrari SF1000 current speed: 195mph
23/08/2020 21:01:35: Ferrari SF1000 current speed: 200mph

2.3. Inheritance

2.3.3. Super Function

In [117]:
# Update sys.path so that it can find our formulapy module
import sys
sys.path.append('examples/formulapy')

# Import our new vehicle domain data model module
from model import Aircraft, Car, RoadVehicle, Vehicle
In [21]:
# Create a new car object
mclaren_p1 = Car(number_engines = 1,
                 engine_horsepower_kw = 673,
                 chassis_height_mm = 1188,
                 chassis_width_mm = 1946,
                 chassis_depth_mm = 4588,
                 make = 'McLaren',
                 model = 'P1',
                 year_manufactured = 2013,
                 maximum_speed_mph = 217,
                 acceleration_rate_mps = 20,
                 deceleration_rate_mps = 50,
                 registration_number = 'MCL P1')

# Print a string representation of the car object
print(mclaren_p1)
Car{'number_engines': 1, 'engine_horsepower_kw': 673, 'chassis_height_mm': 1188, 'chassis_width_mm': 1946, 'chassis_depth_mm': 4588, 'make': 'McLaren', 'model': 'P1', 'year_manufactured': 2013, 'maximum_speed_mph': 217, 'acceleration_rate_mps': 20, 'deceleration_rate_mps': 50, 'mileage_miles': 0, 'speed_mph': 0, 'number_wheels': 4, 'registration_number': 'MCL P1', 'last_mot_date': '23/08/2020'}
In [22]:
# Access an attribute that is set in the Vehicle superclass
print(mclaren_p1.maximum_speed_mph)

# Access an attribute that is set in the RoadVehicle superclass
print(mclaren_p1.last_mot_date)

# Access an attribute that is set in the Car subclass
print(mclaren_p1.number_wheels)
217
23/08/2020
4
In [23]:
# Invoke a method that is defined in the Vehicle superclass
print(mclaren_p1.accelerate())
23/08/2020 21:30:40: McLaren P1 current speed: 0mph
23/08/2020 21:30:41: McLaren P1 current speed: 20mph
23/08/2020 21:30:42: McLaren P1 current speed: 40mph
23/08/2020 21:30:43: McLaren P1 current speed: 60mph
23/08/2020 21:30:44: McLaren P1 current speed: 80mph
23/08/2020 21:30:45: McLaren P1 current speed: 100mph
23/08/2020 21:30:46: McLaren P1 current speed: 120mph
23/08/2020 21:30:47: McLaren P1 current speed: 140mph
23/08/2020 21:30:48: McLaren P1 current speed: 160mph
23/08/2020 21:30:49: McLaren P1 current speed: 180mph
23/08/2020 21:30:50: McLaren P1 current speed: 200mph
23/08/2020 21:30:51: McLaren P1 current speed: 217mph
None
In [24]:
# Invoke a method that is defined in the Car class that itself invokes methods defined in the Vehicle superclass
print(mclaren_p1.avoid_collision())
23/08/2020 21:30:52: McLaren P1 current speed: 217mph
23/08/2020 21:30:53: McLaren P1 current speed: 167mph
23/08/2020 21:30:54: McLaren P1 current speed: 117mph
23/08/2020 21:30:55: McLaren P1 current speed: 67mph
23/08/2020 21:30:56: McLaren P1 current speed: 17mph
23/08/2020 21:30:57: McLaren P1 current speed: 0mph
None

2.3.4. Overriding Methods

In [2]:
# Create a new aircraft object
airbus_a380 = Aircraft(number_engines = 4,
                       engine_horsepower_kw = 670,
                       chassis_height_mm = 24100,
                       chassis_width_mm = 79800,
                       chassis_depth_mm = 72700,
                       make = 'Airbus',
                       model = 'A380',
                       year_manufactured = 2005,
                       maximum_speed_mph = 736,
                       acceleration_rate_mps = 30,
                       deceleration_rate_mps = 30,
                       minimum_speed_mph = 150)

# Print a string representation of the aircraft object
print(airbus_a380)
Aircraft{'number_engines': 4, 'engine_horsepower_kw': 670, 'chassis_height_mm': 24100, 'chassis_width_mm': 79800, 'chassis_depth_mm': 72700, 'make': 'Airbus', 'model': 'A380', 'year_manufactured': 2005, 'maximum_speed_mph': 736, 'acceleration_rate_mps': 30, 'deceleration_rate_mps': 30, 'mileage_miles': 0, 'speed_mph': 0, 'minimum_speed_mph': 150}
In [3]:
# Invoke a method that is defined in the Vehicle superclass
print(airbus_a380.accelerate())
24/08/2020 10:04:06: Airbus A380 current speed: 0mph
24/08/2020 10:04:07: Airbus A380 current speed: 30mph
24/08/2020 10:04:08: Airbus A380 current speed: 60mph
24/08/2020 10:04:09: Airbus A380 current speed: 90mph
24/08/2020 10:04:10: Airbus A380 current speed: 120mph
24/08/2020 10:04:11: Airbus A380 current speed: 150mph
24/08/2020 10:04:12: Airbus A380 current speed: 180mph
24/08/2020 10:04:13: Airbus A380 current speed: 210mph
24/08/2020 10:04:14: Airbus A380 current speed: 240mph
24/08/2020 10:04:15: Airbus A380 current speed: 270mph
24/08/2020 10:04:16: Airbus A380 current speed: 300mph
24/08/2020 10:04:17: Airbus A380 current speed: 330mph
24/08/2020 10:04:18: Airbus A380 current speed: 360mph
24/08/2020 10:04:19: Airbus A380 current speed: 390mph
24/08/2020 10:04:20: Airbus A380 current speed: 420mph
24/08/2020 10:04:21: Airbus A380 current speed: 450mph
24/08/2020 10:04:22: Airbus A380 current speed: 480mph
24/08/2020 10:04:23: Airbus A380 current speed: 510mph
24/08/2020 10:04:24: Airbus A380 current speed: 540mph
24/08/2020 10:04:25: Airbus A380 current speed: 570mph
24/08/2020 10:04:26: Airbus A380 current speed: 600mph
24/08/2020 10:04:27: Airbus A380 current speed: 630mph
24/08/2020 10:04:28: Airbus A380 current speed: 660mph
24/08/2020 10:04:29: Airbus A380 current speed: 690mph
24/08/2020 10:04:30: Airbus A380 current speed: 720mph
24/08/2020 10:04:31: Airbus A380 current speed: 736mph
None
In [4]:
# Invoke a method that is defined in the Aircraft class that overrides a method defined in the Vehicle superclass
print(airbus_a380.brake())
24/08/2020 10:04:53: Airbus A380 current speed: 736mph
24/08/2020 10:04:54: Airbus A380 current speed: 706mph
24/08/2020 10:04:55: Airbus A380 current speed: 676mph
24/08/2020 10:04:56: Airbus A380 current speed: 646mph
24/08/2020 10:04:57: Airbus A380 current speed: 616mph
24/08/2020 10:04:58: Airbus A380 current speed: 586mph
24/08/2020 10:04:59: Airbus A380 current speed: 556mph
24/08/2020 10:05:00: Airbus A380 current speed: 526mph
24/08/2020 10:05:01: Airbus A380 current speed: 496mph
24/08/2020 10:05:02: Airbus A380 current speed: 466mph
24/08/2020 10:05:03: Airbus A380 current speed: 436mph
24/08/2020 10:05:04: Airbus A380 current speed: 406mph
24/08/2020 10:05:05: Airbus A380 current speed: 376mph
24/08/2020 10:05:06: Airbus A380 current speed: 346mph
24/08/2020 10:05:07: Airbus A380 current speed: 316mph
24/08/2020 10:05:08: Airbus A380 current speed: 286mph
24/08/2020 10:05:09: Airbus A380 current speed: 256mph
24/08/2020 10:05:10: Airbus A380 current speed: 226mph
24/08/2020 10:05:11: Airbus A380 current speed: 196mph
24/08/2020 10:05:12: Airbus A380 current speed: 166mph
24/08/2020 10:05:13: Airbus A380 current speed: 150mph
None

2.3.6. Method Resolution Order

In [103]:
# Define a simple superclass
class ClassA:
    
    var1 = 'A'
    
    def __init__(self, a1, a2, a3):
        self.attr1 = a1
        self.attr2 = a2
        self.attr3 = a3
        
    def method1(self):
        return self.attr1 + self.attr2


# Define a simple class derived from ClassA
class ClassB(ClassA):
    
    var1 = 'B'
    
    def __init__(self, b1, b2, b3):
        super().__init__(b1, b2, b3)
        self.attr4 = b2 * b3
        
    def method1(self):
        return self.attr1 * self.attr2


# Define another simple class derived from ClassA
class ClassC(ClassA):
    
    def __init__(self, c1, c2, c3):
        super().__init__(c1, c2, c3)
        self.attr4 = c2 - c3
        
    def method1(self):
        return self.attr1 - self.attr2


# Define a class that is derived from both ClassB and ClassC
class ClassD(ClassB, ClassC):
    pass
In [106]:
# Create and test an instance of ClassB
class_b_object = ClassB(1, 2, 3)
print(f'ClassB attr1: {class_b_object.attr1}')
print(f'ClassB attr2: {class_b_object.attr2}')
print(f'ClassB attr3: {class_b_object.attr3}')
print(f'ClassB attr4: {class_b_object.attr4}')
print(f'ClassB var1: {class_b_object.var1}')
print(f'ClassB method1(): {class_b_object.method1()}')
ClassB attr1: 1
ClassB attr2: 2
ClassB attr3: 3
ClassB attr4: 6
ClassB var1: B
ClassB method1(): 2
In [107]:
# Create and test an instance of ClassC
class_c_object = ClassC(1, 2, 3)
print(f'ClassC attr1: {class_c_object.attr1}')
print(f'ClassC attr2: {class_c_object.attr2}')
print(f'ClassC attr3: {class_c_object.attr3}')
print(f'ClassC attr4: {class_c_object.attr4}')
print(f'ClassC var1: {class_c_object.var1}')
print(f'ClassC method1(): {class_c_object.method1()}')
ClassC attr1: 1
ClassC attr2: 2
ClassC attr3: 3
ClassC attr4: -1
ClassC var1: A
ClassC method1(): -1
In [108]:
# Create and test an instance of ClassD
class_d_object = ClassD(1, 2, 3)
print(f'ClassD attr1: {class_d_object.attr1}')
print(f'ClassD attr2: {class_d_object.attr2}')
print(f'ClassD attr3: {class_d_object.attr3}')
print(f'ClassD attr4: {class_d_object.attr4}')
print(f'ClassD var1: {class_d_object.var1}')
print(f'ClassD method1(): {class_d_object.method1()}')
ClassD attr1: 1
ClassD attr2: 2
ClassD attr3: 3
ClassD attr4: 6
ClassD var1: B
ClassD method1(): 2
In [115]:
# Access the resolved MRO for each class
print(f'ClassA MRO:\n{ClassA.__mro__}\n')
print(f'ClassB MRO:\n{ClassB.__mro__}\n')
print(f'ClassC MRO:\n{ClassC.__mro__}\n')
print(f'ClassD MRO:\n{ClassD.__mro__}\n')
ClassA MRO:
(<class '__main__.ClassA'>, <class 'object'>)

ClassB MRO:
(<class '__main__.ClassB'>, <class '__main__.ClassA'>, <class 'object'>)

ClassC MRO:
(<class '__main__.ClassC'>, <class '__main__.ClassA'>, <class 'object'>)

ClassD MRO:
(<class '__main__.ClassD'>, <class '__main__.ClassB'>, <class '__main__.ClassC'>, <class '__main__.ClassA'>, <class 'object'>)

2.3.7. Finding Subclasses

In [119]:
# List the names of all the subclasses directly derived from the Vehicle class
print([cls.__name__ for cls in Vehicle.__subclasses__()])

# List all the subclasses directly derived from the Vehicle class
print(Vehicle.__subclasses__())
['RoadVehicle', 'Aircraft']
[<class 'model.RoadVehicle'>, <class 'model.Aircraft'>]
In [125]:
# Create a function to list all direct and indirect subclasses of a given class
def find_all_subclasses(cls):
    return set(cls.__subclasses__()).union(
        [subclass for c in cls.__subclasses__() for subclass in find_all_subclasses(c)])

# List all the subclasses that are both directly and indirectly derived from the Vehicle class
print(find_all_subclasses(Vehicle))
{<class 'model.RoadVehicle'>, <class 'model.Aircraft'>, <class 'model.Car'>}

2.4. Constructors

2.4.2. Non-Parameterized Constructor

In [128]:
# Define a class with a non-parameterized constructor
class ClassX:
    
    def __init__(self):
        print(f'Creating an instance of {type(self).__name__}...')
        
    def sum(self, a, b):
        return a + b
    
# Create a new instance of ClassX
class_x_object = ClassX()

# Invoke a method on the new object of type ClassX
print(class_x_object.sum(100, 38))
Creating an instance of ClassX...
138

2.4.3. Default Constructor

In [129]:
# Define a class without an explicit constructor
class ClassY:
    
    def product(self, a, b):
        return a * b

# Create a new instance of ClassY
class_y_object = ClassY()

# Invoke a method on the new object of type ClassY
print(class_y_object.product(13, 20))
260

2.4.4. Explicit Invocation

In [136]:
# Define a class with a constructor that explicitly invokes the constructor of another class
class ClassZ:
    
    def __init__(self):
        ClassX.__init__(self)
        
    def modulus(self, a, b):
        return a % b

# Create a new instance of ClassZ
class_z_object = ClassZ()

# Invoke a method on the new object of type ClassZ
print(class_z_object.modulus(103, 4))
Creating an instance of ClassZ...
3

2.5. Name Mangling

In [138]:
# Define a class that contains 'private' variables
class User:
    
    def __init__(self, fname, lname, email, dob, postal_address):
        self.fname = fname
        self.lname = lname
        self.email = email
        self.__dob = dob
        self.__postal_address = postal_address
        
    def display_dob(self):
        print(self.__dob)
        
    def display_postal_address(self):
        print(self.__postal_address)

# Create a new user
barack_obama_user = User('Barack', 'Obama', '[email protected]', '04/08/1961', '1600 Pennsylvania Avenue')

# Display the user's private dob using the display_dob() method
barack_obama_user.display_dob()
04/08/1961
In [140]:
# Display the user's private dob by directly accessing the dob attribute using dot notation
print(barack_obama_user.dob)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-140-f90fc1caef8b> in <module>
      1 # Display the user's private dob by directly accessing the dob attribute using dot notation
----> 2 print(barack_obama_user.dob)

AttributeError: 'User' object has no attribute 'dob'
In [141]:
# Display the user's private dob indirectly by accessing the mangled dob attribute
print(barack_obama_user._User__dob)
04/08/1961

2.6. Common Functions

In [180]:
# Create a new aircraft object
airbus_a380 = Aircraft(number_engines = 4,
                       engine_horsepower_kw = 670,
                       chassis_height_mm = 24100,
                       chassis_width_mm = 79800,
                       chassis_depth_mm = 72700,
                       make = 'Airbus',
                       model = 'A380',
                       year_manufactured = 2005,
                       maximum_speed_mph = 736,
                       acceleration_rate_mps = 30,
                       deceleration_rate_mps = 30,
                       minimum_speed_mph = 150)

# hasattr()
print( hasattr(airbus_a380, 'engine_horsepower_kw') )
print( hasattr(airbus_a380, 'last_mot_date') )
print()

# type()
print( type(airbus_a380) )
print( type([1, 2, 3]) )
print( type(('a', 'b', 'c')) )
print( type({1: 'a', 2: 'b', 3: 'c'}) )
print( type(Vehicle) )
print( type(str) )
print( type(int) )
print( type(str) )
print( type('Hello World!') )
print( type(38) )
print( type(0.5) )
print( type(True) )
print( type(False) )
print( type(47 & 55) )
print( type(None) )
print()

# issubclass()
print( issubclass(type(airbus_a380), Vehicle) )
print( issubclass(Aircraft, Vehicle) )
print( issubclass(Vehicle, Aircraft) )
print( issubclass(Vehicle, Vehicle) )
print( issubclass(Vehicle, object) )
print( issubclass(str, object) )
print( issubclass(list, object) )
print( issubclass(tuple, object) )
print( issubclass(dict, object) )
print( issubclass(type(ClassD(1, 2, 3)), ClassA) )
print( issubclass(type(None), ClassA) )
print( issubclass(type(None), object) )
print()

# isinstance()
print( isinstance(airbus_a380, Aircraft) )
print( isinstance(airbus_a380, Vehicle) )
print( isinstance(airbus_a380, object) )
print( isinstance(airbus_a380, Car) )
print( isinstance(None, object) )
True
False

<class 'model.Aircraft'>
<class 'list'>
<class 'tuple'>
<class 'dict'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'str'>
<class 'int'>
<class 'float'>
<class 'bool'>
<class 'bool'>
<class 'int'>
<class 'NoneType'>

True
True
False
True
True
True
True
True
True
True
False
True

True
True
True
False
True