Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
58 views

1.OOPS Class Objects Inheritance Encapsulation Polymorphism

Here are the steps to add a report method that prints the student name and id along with a passed in score parameter: 1. Define the report method and include self and a score parameter 2. Call the say_name method using self to print the name 3. Print the id attribute using self 4. Print the passed in score parameter This allows accessing and reusing the existing name printing logic while also including the additional id and score fields in the report. 7.2.2 OBJECT An object is an instance of a class. To create an object, we use the class name followed by parentheses. This is called instantiation. The syntax is: object_name = ClassName(arguments

Uploaded by

tanmayeesamal9
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
58 views

1.OOPS Class Objects Inheritance Encapsulation Polymorphism

Here are the steps to add a report method that prints the student name and id along with a passed in score parameter: 1. Define the report method and include self and a score parameter 2. Call the say_name method using self to print the name 3. Print the id attribute using self 4. Print the passed in score parameter This allows accessing and reusing the existing name printing logic while also including the additional id and score fields in the report. 7.2.2 OBJECT An object is an instance of a class. To create an object, we use the class name followed by parentheses. This is called instantiation. The syntax is: object_name = ClassName(arguments

Uploaded by

tanmayeesamal9
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

OBJECT-ORIENTED PROGRAMMING

7
7.1 Introduction to OOP ................................................................................................... 121
7.2 Class and Object ...................................................................................................... 123
7.2.1 Class .................................................................................................... 123
7.2.2 Object ................................................................................................... 124
7.2.3 Class vs Instance Attributes ......................................................................... 125
7.3 Inheritance, Encapsulation, and Polymorphism.................................................................. 127
7.3.1 Inheritance ............................................................................................. 127
7.3.1.1 Inheriting and Extending New Method ........................................................ 128
7.3.1.2 Inheriting and Method Overriding .............................................................. 129
7.3.1.3 Inheriting and Updating Attributes With Super .............................................. 129
7.3.2 Encapsulation.......................................................................................... 130
7.3.3 Polymorphism.......................................................................................... 132
7.4 Summary and Problems .............................................................................................. 132
7.4.1 Summary ............................................................................................... 132
7.4.2 Problems ............................................................................................... 132

7.1 INTRODUCTION TO OOP


So far, all the codes we have written belong to the category of procedure-oriented programming
(POP), which consists of a list of instructions to tell the computer what to do; these instructions are
then organized into functions. The program is divided into a collection of variables, data structures,
and routines to accomplish different tasks. Python is a multiparadigm programming language, that is,
it supports different programming approaches. One way to program in Python is to use object-oriented
programming (OOP). The learning curve is steeper, but it is extremely powerful and worth the time
invested in mastering it. Note that you do not have to use OOP when programming in Python. You
can still write very powerful programs using the POP. That said, the POP is good for simple and small
programs, while the OOP is better suited for large programs. Let us take a closer look at object-oriented
programming.
The object-oriented programming breaks the programming task into objects, which combine data
(known as attributes) and behaviors/functions (known as methods). Thus, there are two main compo-
nents of the OOP: class and object.
The class is a blueprint to define a logical grouping of data and functions. It provides a way to
create data structures that model real-world entities. For example, we can create a people class that
contains the data such as name, age, and some behavior functions to print out ages and genders of a
group of people. While class is the blueprint, an object is an instance of the class with actual values.
122 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

For example, a person named “Iron man” with age 35. Put it another way, a class is like a template
to define the needed information, and an object is one specific copy that filled in the template. Also,
objects instantiated from the same class are independent from each other. For example, if we have
another person, say, “Batman” with age 33, it can be instantiated from the people class, but it remains
an independent instance.
To implement the above example in Python, see the code below. Do not worry if you do not under-
stand the syntax; the next section provides more helpful examples.
In [1]: class People():
def __init__(self, name, age):
self.name = name
self.age = age

def greet(self):
print("Greetings, " + self.name)

In [2]: person1 = People(name = "Iron Man", age = 35)


person1.greet()
print(person1.name)
print(person1.age)

Greetings, Iron Man


Iron Man
35

In [3]: person2 = People(name = "Batman", age = 33)


person2.greet()
print(person2.name)
print(person2.age)

Greetings, Batman
Batman
33

In the above code example, we first defined a class, People, with name and age as the data, and a
method greet. We then instantiated an object, person1 with the specific name and age. We can clearly
see that the class defines the whole structure, while the object is just an instance of the class.
The many benefits of using OOP are as follows: It provides a clear modular structure for pro-
grams that enhances code reusability. It provides a simple way to solve complex problems. It helps
define more abstract data types to model real-world scenarios. It hides implementation details, leaving
a clearly defined interface. It combines data and operations.
There are also other advantages of using OOP in a large project. We encourage you to search online
to find out more. At this point, you may still not fully understand OOP’s advantages until you are
involved in complex large projects. We will continue to learn more about OOP during the course of
this book, and its usefulness will become apparent.
7.2 CLASS AND OBJECT 123

7.2 CLASS AND OBJECT


The previous section introduced the two main components of OOP: class, which is a blueprint used to
define a logical grouping of data and functions, and object, which is an instance of the defined class
with actual values. In this section, we will get into greater detail of both of these components.

7.2.1 CLASS
A class is a definition of the structure that we want. Similar to a function, it is defined as a block of
code, starting with the class statement. The syntax of defining a class is:

class ClassName(superclass):

def __init__(self, arguments):


# define or assign object attributes

def other_methods(self, arguments):


# body of the method

Note that the definition of a class is very similar to a function. It needs to be instantiated first before
you can use it. For the class name, it is standard convention to use "CapWords." The superclass is used
when you want create a new class to inherit the attributes and methods from another already defined
class. We will talk more about inheritance in the next section. The __init__ is one of the special
methods in Python classes that is run as soon as an object of a class is instantiated (created). It assigns
initial values to the object before it is ready to be used. Note the two underscores at the beginning and
end of the init, indicating this is a special method reserved for special use in the language. In this init
method, you can assign attributes directly when you create the object. The other_methods functions
are used to define the instance methods that will be applied on the attributes, just like functions we
discussed before. You may notice that there is an argument self for defining this method in the class.
Why? A class instance method must have this extra argument as the first argument when you define
it. This particular argument refers to the object itself; conventionally, we use self to name it. Instance
methods can freely access attributes and other methods in the same object by using this self parameter.
See the example below.

EXAMPLE: Define a class named Student, with the attributes sid (student id), name, gender,
type in the init method, and a method called say_name to print out the student’s name. All
attributes will be passed in except type, which will have a value as "learning".

In [1]: class Student():

def __init__(self, sid, name, gender):


self.sid = sid
self.name = name
self.gender = gender
124 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

self.type = "learning"

def say_name(self):
print("My name is " + self.name)

From the above example, we can see this simple class contains all the necessary parts mentioned
previously. The __init__ method will initialize the attributes when we create an object. We need to
pass in the initial value for sid, name, and gender, while the attribute type is a fixed value as "learning".
These attributes can be accessed by all the other methods defined in the class with self.attribute, for
example, in the say_name method, we can use the name attribute with self.name. The methods defined
in the class can be accessed and used in other different methods as well using self.method. Let us see
the following example.

TRY IT! Add a method report that prints not only the student name, but also the student id. The
method will have another parameter, score, that will pass in a number between 0 and 100 as part
of the report.

In [2]: class Student():

def __init__(self, sid, name, gender):


self.sid = sid
self.name = name
self.gender = gender
self.type = "learning"

def say_name(self):
print("My name is " + self.name)

def report(self, score):


self.say_name()
print("My id is: " + self.sid)
print("My score is: " + str(score))

7.2.2 OBJECT
As mentioned before, an object is an instance of the defined class with actual values. Many instances of
different values associated with the class are possible, and each of these instances will be independent
with each other (as seen previously). Also, after we create an object and call this instance method from
the object, we do not need to give value to the self parameter because Python automatically provides
it; see the following example.
7.2 CLASS AND OBJECT 125

EXAMPLE: Create two objects ("001", "Susan", "F") and ("002", "Mike", "M"), and call
the method say_name.

In [3]: student1 = Student("001", "Susan", "F")


student2 = Student("002", "Mike", "M")

student1.say_name()
student2.say_name()
print(student1.type)
print(student1.gender)

My name is Susan
My name is Mike
learning
F

In the above code, we created two objects, student1 and student2, with two different sets of values.
Each object is an instance of the Student class and has a different set of attributes. Type student1.+TAB
to see the defined attributes and methods. To get access to one attribute, type object.attribute, e.g.,
student1.type. In contrast, to call a method, you need the parentheses because you are calling a
function, such as student1.say_name().

TRY IT! Call method report for student1 and student2 with scores of 95 and 90, respectively.
Note that we do not need the “self” as an argument here.

In [4]: student1.report(95)
student2.report(90)

My name is Susan
My id is: 001
My score is: 95
My name is Mike
My id is: 002
My score is: 90

We can see both methods calling to print out the data associated with the two objects. Note that the
score value we passed in is only available to the method report (within the scope of this method). We
can also see that the method say_name call in the report also works, as long as you call the method
with the self in it.

7.2.3 CLASS VS INSTANCE ATTRIBUTES


The attributes we presented above are actually called instance attributes, which means that they are
only belong to a specific instance; when you use them, you need to use the self.attribute within
126 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

the class. There are other attributes called class attributes, which will be shared with all the instances
created from this class. Let us see an example how to define and use a class attribute.

EXAMPLE: Modify the Student class to add a class attribute n, which will record how many
object we are creating. Also, add a method num_instances to print out the number.

In [5]: class Student():

n = 0

def __init__(self, sid, name, gender):


self.sid = sid
self.name = name
self.gender = gender
self.type = "learning"
Student.n += 1

def say_name(self):
print("My name is " + self.name)

def report(self, score):


self.say_name()
print("My id is: " + self.sid)
print("My score is: " + str(score))

def num_instances(self):
print(f"We have {Student.n}-instance in total")

In defining a class attribute, we must define it outside of all the other methods without using
self. To use the class attributes, we use ClassName.attribute, which in this case is Student.n. This
attribute will be shared with all the instances that are created from this class. Let us see the following
code to show the idea.
In [6]: student1 = Student("001", "Susan", "F")
student1.num_instances()
student2 = Student("002", "Mike", "M")
student1.num_instances()
student2.num_instances()

We have 1-instance in total


We have 2-instance in total
We have 2-instance in total

As before, we created two objects, the instance attribute sid, name, but gender only belongs to the
specific object. For example, student1.name is “Susan” and student2.name is “Mike”. But when we
7.3 INHERITANCE, ENCAPSULATION, AND POLYMORPHISM 127

print the class attribute Student.n_instances out after we created object student2, the one in the
student1 changes as well. This is the expectation we have for the class attribute because it is shared
across all the created objects.
Now that we understand the difference between class and instance, we are in good shape to use basic
OOP in Python. Before we can take full advantage of OOP, we still need to understand the concept of
inheritance, encapsulation, and polymorphism. Let us start the next section!

7.3 INHERITANCE, ENCAPSULATION, AND POLYMORPHISM


We have already seen the modeling power of OOP using the class and object functions by combin-
ing data and methods. There are three more important concepts: (1) inheritance, which makes the
OOP code more modular, easier to reuse, and capable of building a relationship between classes;
(2) encapsulation, which can hide some of the private details of a class from other objects; and
(3) polymorphism, which allows us to use a common operation in different ways. A brief discussion
of how these concepts is discussed below.

7.3.1 INHERITANCE
Inheritance allows us to define a class that inherits all the methods and attributes from another class.
Convention denotes the new class as child class, and the one that it inherits from is called the parent
class or superclass. If we refer back to the definition of class structure, we can see the structure for
basic inheritance is class ClassName(superclass), which means the new class can access all the
attributes and methods from the superclass. Inheritance builds a relationship between the child and
parent classes. Usually, the parent class is a general type while the child class is a specific type. An
example is presented below.

TRY IT! Define a class named Sensor with attributes name, location, and record_date that pass
from the creation of an object and an attribute data as an empty dictionary to store data. Create
one method add_data with t and data as input parameters to take in timestamp and data arrays.
Within this method, assign t and data to the data attribute with “time” and “data” as the keys. In
addition, create one clear_data method to delete the data.

In [1]: class Sensor():


def __init__(self, name, location, record_date):
self.name = name
self.location = location
self.record_date = record_date
self.data = {}

def add_data(self, t, data):


self.data["time"] = t
128 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

self.data["data"] = data
print(f"We have {len(data)} points saved")

def clear_data(self):
self.data = {}
print("Data cleared!")

Now we have a class to store general sensor information, we can create a sensor object to store data.

EXAMPLE: Create a sensor object.

In [2]: import numpy as np

sensor1 = Sensor("sensor1", "Berkeley", "2019-01-01")


data = np.random.randint(-10, 10, 10)
sensor1.add_data(np.arange(10), data)
sensor1.data

We have 10 points saved

Out[2]: {"time": array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),


"data": array([ 3, 2, 5, -1, 2, -2, 6, -1, 5, 4])}

7.3.1.1 Inheriting and Extending New Method


Suppose we have one different type of sensor, an accelerometer. It shares the same attributes and
methods as Sensor class, but it also has different attributes or methods that need to be appended or
modified from the original class. What should we do? Do we create a different class from scratch? This
is where inheritance can be used to make life easier. This new class will inherit from the Sensor class
with all the attributes and methods. We can think whether we want to extend the attributes or methods.
Let us first create this new class, Accelerometer, and add a new method, show_type, to report what
kind of sensor it is.
In [3]: class Accelerometer(Sensor):

def show_type(self):
print("I am an accelerometer!")

acc = Accelerometer("acc1", "Oakland", "2019-02-01")


acc.show_type()
data = np.random.randint(-10, 10, 10)
acc.add_data(np.arange(10), data)
acc.data
7.3 INHERITANCE, ENCAPSULATION, AND POLYMORPHISM 129

I am an accelerometer!
We have 10 points saved

Out[3]: {"time": array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),


"data": array([ -1, 4, 7, -10, -2, -6, 2, -8, 9, 3])}

Creating this new Accelerometer class is very simple. It inherits from Sensor (denoted as a su-
perclass), and the new class actually contains all the attributes and methods from the superclass. We
then add a new method, show_type, which does not exist in the Sensor class, but we can successfully
extend the child class by adding the new method. This shows the power of inheritance: we have reused
most part of the Sensor class in a new class, and extended the functionality. Basically, the inheritance
sets up a logical relationship for the modeling of the real-world entities: the Sensor class as the parent
class is more general and passes all the characteristics to the child class Accelerometer.

7.3.1.2 Inheriting and Method Overriding


When we inherit from a parent class, we can change the implementation of a method provided by the
parent class; this is called method overriding and is shown in the example below.

EXAMPLE: Create a class UCBAcc (a specific type of accelerometer that was created at UC
Berkeley) that inherits from Accelerometer but replaces the show_type method that also prints
out the name of the sensor.

In [4]: class UCBAcc(Accelerometer):

def show_type(self):
print(f"I am {self.name}, created at Berkeley!")

acc2 = UCBAcc("UCBAcc", "Berkeley", "2019-03-01")


acc2.show_type()

I am UCBAcc, created at Berkeley!

We see that our new UCBAcc class actually overrides the method show_type with new features. In
this example, we are not only inheriting features from our parent class, but we are also modifying/im-
proving some methods.

7.3.1.3 Inheriting and Updating Attributes With Super


Let us create a class NewSensor that inherits from Sensor class, but is updated the attributes by adding
a new attribute brand. Of course, we can redefine the whole __init__ method as shown below that is
capable of overriding the parent function.
130 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

In [5]: class NewSensor(Sensor):


def __init__(self,name,location,record_date,brand):
self.name = name
self.location = location
self.record_date = record_date
self.brand = brand
self.data = {}

new_sensor = NewSensor("OK", "SF", "2019-03-01", "XYZ")


new_sensor.brand

Out[5]: "XYZ"

There is a better way to achieve the same result. If we use the super method, we can avoid referring
to the parent class explicitly, as shown in the following example:

EXAMPLE: Redefine the attributes in inheritance.

In [6]: class NewSensor(Sensor):


def __init__(self,name,location,record_date,brand):
super().__init__(name, location, record_date)
self.brand = brand

new_sensor = NewSensor("OK", "SF", "2019-03-01", "XYZ")


new_sensor.brand

Out[6]: "XYZ"

Now we can see with the super method, we have avoided listing all of the definition of the attributes;
this helps keep your code maintainable for the foreseeable future. Because the child class does not
implicitly call the __init__ of the parent class, we must use super().__init__, as shown above.

7.3.2 ENCAPSULATION
Encapsulation is one of the fundamental concepts in OOP. It describes the idea of restricting access to
methods and attributes in a class. Encapsulation hides complex details from the users and prevents data
being modified by accident. In Python, this is achieved by using private methods or attributes using the
underscore as prefix, i.e., single “_” or double “__”, as shown the following example.

EXAMPLE:

In [7]: class Sensor():


def __init__(self, name, location):
self.name = name
7.3 INHERITANCE, ENCAPSULATION, AND POLYMORPHISM 131

self._location = location
self.__version = "1.0"

# a getter function
def get_version(self):
print(f"The sensor version is {self.__version}")

# a setter function
def set_version(self, version):
self.__version = version

In [8]: sensor1 = Sensor("Acc", "Berkeley")


print(sensor1.name)
print(sensor1._location)
print(sensor1.__version)

Acc
Berkeley

--------------------------------------------------------

AttributeError Traceback (most recent call last)

<ipython-input-8-ca9b481690ba> in <module>
2 print(sensor1.name)
3 print(sensor1._location)
----> 4 print(sensor1.__version)

AttributeError: 'Sensor' object has no attribute '__version'

The above example shows how the encapsulation works. With single underscore, we defined a
private variable that should not be accessed directly. Note that this is convention and nothing stops you
from actually accessing it. With double underscores, we can see that the attribute __version cannot be
accessed or modified directly. To get access to the double underscore attributes, we need to use “getter”
and “setter” functions to access it internally. A “getter” function is shown in the following example.

In [9]: sensor1.get_version()

The sensor version is 1.0


132 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

In [10]: sensor1.set_version("2.0")
sensor1.get_version()

The sensor version is 2.0

The single and double underscore(s) apply to private methods as well, which are not discussed
because they are similar to the private attributes.

7.3.3 POLYMORPHISM
Polymorphism is another fundamental concept in OOP, which means multiple forms. Polymorphism
allows the use of a single interface with different underlying forms, such as data types or classes. For
example, we can have commonly named methods across classes or child classes. We have already seen
one example above when we overrode the method show_type in the UCBAcc. Both the parent class
Accelerometer and child class UCBAcc have a method named show_type, but they are implemented
differently. This ability of using a single name with many forms acting differently in different situations
greatly reduces our complexities. We will not expand this discussion on polymorphism, if you are
interested, check more online to get a deeper understanding.

7.4 SUMMARY AND PROBLEMS


7.4.1 SUMMARY
1. OOP and POP are different. OOP has many benefits and is often more appropriate for use in large-
scale projects.
2. Class is the blueprint of the structure that allows us to group data and methods, while object is an
instance from the class.
3. The concept of “inheritance” is key to OOP, which allows us to refer attributes or methods from the
superclass.
4. The concept of “encapsulation” allows us to hide some of the private details of a class from other
objects.
5. The concept of “polymorphism” allows us to use a common operation in different ways for different
data input.

7.4.2 PROBLEMS
1. Describe the differences between classes and objects.
2. Describe why we use “self” as the first argument in a method.
3. What is a constructor? And why do we use it?
4. Describe the differences between class and instance attributes.
5. The following is a definition of the class Point that takes in the coordinates x, y. Add a method
plot_point that plots the position of a point.
7.4 SUMMARY AND PROBLEMS 133

import matplotlib.pyplot as plt

class Point():
def __init__(self, x, y):
self.x = x
self.y = y

6. Use the class from Problem 5 and add a method calculate_dist which takes in x and y from
another point, and returns the distance calculated between the two points.
7. What’s inheritance?
8. How do we inherit from a superclass and add new methods?
9. When we inherit from a superclass, we need to replace a method with a new one; how do we do
that?
10. What’s the super method? Why do we need it?
11. Create a class to model some real world objects and create a new class to inherit from it. See the
example below. You should use a different example and incorporate as many of the concepts we’ve
learned so far as possible.
In [1]: class Car():
def __init__(self, brand, color):
self.brand = brand
self.color = color

def start_my_car(self):
print("I am ready to drive!")

class Truck(Car):
def __init__(self, brand, color, size):
super().__init__(brand, color)
self.size = size

def start_my_car(self, key):


if key == "truck_key":
print("I am ready to drive!")
else:
print("Key is not right")

def stop_my_car(self, brake):


if brake:
print("The engine is stopped!")
else:
print("I am still running!")
134 CHAPTER 7 OBJECT-ORIENTED PROGRAMMING

truck1 = Truck("Toyota", "Silver", "Large")


truck1.start_my_car("truck_key")
truck1.stop_my_car(brake = False)

You might also like