Object Oriented Programming (OOP)¶
Object-oriented Programming, or OOP, is a programming paradigm which provides a means of structuring programs so that properties(attributes) and behaviors(methods) are bundled into individual objects.
For example, an object could represent a car with properties like name, color, wheels etc. and with behaviors like accelerate, brake, steer etc. An object could also represent a person with properties like name, age, sex, etc and with behaviours like talking, eating, walking, running etc.
Another common programming paradigm is procedural programming which structures a program like a recipe in that it provides a set of steps, in the form of functions and code blocks, which flow sequentially in order to complete a task.
The key takeaway is that objects are at the center of the object-oriented programming paradigm, not only representing the data, as in procedural programming, but in the overall structure of the program as well.
Namespaces and Scopes¶
Namespaces¶
Is basically a system to make sure that all the names in a program are unique and can be used without any conflict.
Interesting fact: Python implements namespaces as dictionaries
There is a name-to-object mapping, with the names as keys and the objects as values.
Multiple namespaces can use the same name and map it to a different object.
Name: an unique identifier & Space: something related to scope.
Types of Namespace¶
Local namespace includes local names inside a function.
Global namespace includes names from various imported modules that you are using in a project.
Built-in namespace includes built-in functions and built-in exception names.
Examples:¶
# var1 is in the global namespace
var1 = 5
def some_func():
# var2 is in the local namespace
var2 = 6
def some_inner_func():
# var3 is in the nested local
# namespace
var3 = 7
num_of_people = 5
def add_people():
global num_of_people
num_of_people = num_of_people + 1
print(num_of_people)
add_people()
6
dir() function¶
Without arguments, return the list of names in the current local scope.
With an argument, attempt to return a list of valid attributes for that object.
print(dir())
['In', 'Out', '_', '__', '___', '__builtin__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dh', '_i', '_i1', '_i2', '_i3', '_ih', '_ii', '_iii', '_oh', 'add_people', 'exit', 'get_ipython', 'num_of_people', 'quit', 'some_func', 'var1']
print(dir(add_people))
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
Scopes¶
Namespace uniquely identifies names in a program, but it does’t allow us to use a variable anywhere we want.
Scopes are region where a Python’s object are accessible without any prefix.
Types of Scopes¶
During execution of a program, following scope exists:
innermost scope, which is searched first, contains the local names
scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
next-to-last scope contains the current module’s global names
outermost scope (searched last) is the namespace containing built-in names
Example¶
def some_func():
print("Inside some_func")
def some_inner_func():
var = 10
print("Inside inner function, value of var:",var)
some_inner_func()
print("Try printing var from outer function: ",var)
some_func()
Inside some_func
Inside inner function, value of var: 10
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-5-5fc192decd15> in <module>
6 some_inner_func()
7 print("Try printing var from outer function: ",var)
----> 8 some_func()
<ipython-input-5-5fc192decd15> in some_func()
5 print("Inside inner function, value of var:",var)
6 some_inner_func()
----> 7 print("Try printing var from outer function: ",var)
8 some_func()
NameError: name 'var' is not defined
a_num = 10
b_num = 11
def outer_func():
global a_num
a_num = 15
b_num = 16
def inner_func():
global a_num
a_num = 20
b_num = 21
print('a_num inside inner_func :', a_num)
print('b_num inside inner_func :', b_num)
inner_func()
print('a_num inside outer_func :', a_num)
print('b_num inside outer_func :', b_num)
outer_func()
print('a_num outside all functions :', a_num)
print('b_num outside all functions :', b_num)
a_num inside inner_func : 20
b_num inside inner_func : 21
a_num inside outer_func : 20
b_num inside outer_func : 16
a_num outside all functions : 20
b_num outside all functions : 11
Class in Python¶
User-defined datastructure used to define arbitrary information about something.
For example, a
Car()class can have properties like name, type etc.Note: Class is just like a blueprint, it doen’t hold any state.
Object in Python¶
While class is a blueprint, an Object (Instance) is a copy of class with real values.
Its like a Car with real name ‘i20’ and model ‘2019’
A Glance at Classes¶
Defining Class¶
class ClassName:
<statement-1>
.
.
.
<statement-N>
class PythonClass:
pass # pass is like a place holder where the code will enventually be written.
Instance Attributes¶
All classes create objects, and all objects contain characteristics called attributes (sometimes referred to as properties)
Use the
__init__()method to initialize the object’s initial attributes. (State)__init__()method must have at least one argumentself, which refers to object itself.
class Car:
# Instance Attributes
def __init__(self, name, unid):
self.name = name
self.unid = unid
Note: We never call the init() method; it gets autmatically called when a new instance is created.
Class Attributes¶
Shared by all the instances of the class.
Class attributes remain the same for all instances.
class Car:
# Class Attribute
type = 'vehicle'
# Instance Attributes
def __init__(self, name, unid):
self.name = name
self.unid = unid
Intantiate an Object(s) from a Class¶
means creating a new, unique instance of a class
class Car:
pass
Car()
<__main__.Car at 0x1db880825e0>
Car()
<__main__.Car at 0x1db88082f40>
car_A = Car()
car_B = Car()
car_A == car_B
False
Instance Methods¶
Define inside a class, and can be used to get the content or perform operation on an instance/attributes.
Similar to
__init__()method, they also haveselfvariable
class Car:
# Class Attribute
type = 'vehicle'
# Instance Attributes
def __init__(self, name, unid):
self.name = name
self.unid = unid
# An instance method
def description(self):
return "Car name is {} and its ID is {}".format(self.name, self.unid)
# An instance method
def honk(self, level=1):
return "Car is honking at level {}".format(level)
# Instantiate Car object
i20 = Car("i20", 1123)
# Call instance method
print(i20.description())
print(i20.honk(2))
Car name is i20 and its ID is 1123
Car is honking at level 2
"My name is {name}, and My age is {age}".format(name='Sajal', age=10)
'My name is Sajal, and My age is 10'
Attribute lookup priotizes the instance, when same attribute name occurs. See below:
class Person:
name = 'Kshitiz Maharjan'
sex = 'Male'
p1 = Person()
print(p1.name, p1.sex)
p2 = Person()
p2.sex = 'Female'
print(p2.name, p2.sex)
print(p1.name, p1.sex)
Kshitiz Maharjan Male
Kshitiz Maharjan Female
Kshitiz Maharjan Male
Modify Attributes¶
class Email:
def __init__(self):
self.is_sent = False
def send_email(self, message):
print("Sending:", message)
self.is_sent = True
email = Email()
email.is_sent
False
email.send_email("Ehh Bhaiii, K chhha?")
email.is_sent
Sending: Ehh Bhaiii, K chhha?
True
Inheritance¶
One class takes attributes and methods of another.
Child class derived from Parent class
Child class override or extend attirbutes and methods of parent’s class.
Syntax:¶
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
class Car:
# Class attributes
type = 'vehicle'
# Instance Attributes / Initialize
def __init__(self, name, manufacturer, year):
self.name = name
self.manufacturer = manufacturer
self.year = year
# instance method
def accelerate(self, speed):
return "Car %s is acclerating at %d" % (self.name, speed)
# Child class of Car
class SuperCar(Car):
def zoom(self):
return "Car %s zoomed" % (self.name)
i20 = Car('i20', 'Hyundai', 2020)
print(i20.type)
print(i20.name)
print(i20.year)
print(i20.accelerate(2))
vehicle
i20
2020
Car i20 is acclerating at 2
lamborghini = SuperCar('Huracan', 'Lamborghini', 2019)
print(lamborghini.type)
print(lamborghini.name)
print(lamborghini.year)
print(lamborghini.accelerate(10))
print(lamborghini.zoom())
vehicle
Huracan
2019
Car Huracan is acclerating at 10
Car Huracan zoomed
Multiple Inheritance¶
Python also supports multiple inheritance.
Syntax:¶
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
If attribute not found in
DerivedClassName, it is searched inBase1If not found there, it is searched in
Base2, and the search goes on.
Override¶
Child classes can override attributes and methods of parent clasess.
See example below:
class Car:
# Class attributes
type = 'vehicle'
# Instance Attributes / Initialize
def __init__(self, name, manufacturer, year):
self.name = name
self.manufacturer = manufacturer
self.year = year
# instance method
def accelerate(self, speed):
return "Car %s is acclerating at %d" % (self.name, speed)
# Child class of Car
class SuperCar(Car):
type = 'Luxury vehicle'
def zoom(self):
return "Car %s zoomed" % (self.name)
i20 = Car('i20', 'Hyundai', 2020)
print(i20.type)
lamborghini = SuperCar('Huracan', 'Lamborghini', 2019)
print(lamborghini.type)
vehicle
Luxury vehicle
class Car:
# Class attributes
type = 'vehicle'
# Instance Attributes / Initialize
def __init__(self, name, manufacturer, year):
self.name = name
self.manufacturer = manufacturer
self.year = year
# instance method
def accelerate(self, speed):
return "Car %s is acclerating at %d" % (self.name, speed)
# Child class of Car
class SuperCar(Car):
type = 'Luxury vehicle'
def __init__(self, name, manufacturer, year, hyper=True):
super().__init__(name, manufacturer, year)
self.hyper = hyper
def accelerate(self, speed):
return super().accelerate(speed*2)
def zoom(self):
return "Car %s zoomed" % (self.name)
i20 = Car('i20', 'Hyundai', 2020)
print(i20.accelerate(10))
lamborghini = SuperCar('Huracan', 'Lamborghini', 2019, True)
print(lamborghini.accelerate(10))
Car i20 is acclerating at 10
Car Huracan is acclerating at 20
super() in-depth¶
revisit later
isinstance()¶
determines if an instance is also an instance of a certain parent class.
isinstance(lamborghini, Car)
True
isinstance(lamborghini, SuperCar)
True
isinstance(1, int)
True
x = 4.2
isinstance(x, (int, float))
True
Private Variables¶
class Car:
# Instance Attributes / Initialize
def __init__(self, name, manufacturer, year):
self.name = name
self.manufacturer = manufacturer
self.year = year
self.__type = 'vehicle'
self._num_of_wheels = 4
def get_type(self):
return self.__type
# instance method
def accelerate(self, speed):
return "Car %s is acclerating at %d" % (self.name, speed)
i20 = Car('i20', 'Hyundai', 2020)
i20.get_type()
'vehicle'
i20.__type
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-37-e31da153b0a7> in <module>
----> 1 i20.__type
AttributeError: 'Car' object has no attribute '__type'
i20._Car__type
'vehicle'
i20._num_of_wheels
4
Name Mangling¶
Note : Revisit after automation module.
Is Python OOPs?¶
Revisit Later
Exercise¶
Implement pow(x, n) using class
Reverse a string word by word: Input: hello world Output: world hello
Reverse a string in word by word. Example: Input: hello world Output: dlrow olleh
A class named
Circleconstructed by a radius and two methods which will compute the area and the perimeter of a circle.A class which has two methods
get_Stringandprint_String.get_Stringaccept a string from the user andprint_Stringprint the string in upper caseCreate a
Cricleclass and intialize it withradius. Make two methodsget_areaandget_circumferenceinside this class.Create
classnamed Shape which has attributes length and breath and a method area, which returns the area of the shape [Assuming area to be length * breath]Create a
classnamedDogwhich has instance attributesname,breed,life_expectancy,origin_placeand class attributespecies. Add instance methodbark(),eat(),description()methods.