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 haveself
variable
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 inBase1
If 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
Circle
constructed by a radius and two methods which will compute the area and the perimeter of a circle.A class which has two methods
get_String
andprint_String
.get_String
accept a string from the user andprint_String
print the string in upper caseCreate a
Cricle
class and intialize it withradius
. Make two methodsget_area
andget_circumference
inside this class.Create
class
named 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
class
namedDog
which has instance attributesname
,breed
,life_expectancy
,origin_place
and class attributespecies
. Add instance methodbark()
,eat()
,description()
methods.