Object Oriented Programming – Intro
What’s OOP ?
Procedural vs Object Oriented
- Procedural programming :
- sequential coding
- OK for Data Analysis
- Object Oriented
- Coding as interaction for object
- Frameworks and tools
- usability
Terminology
- Object = state + behaviour ==> a FIlou a dog has 4 legs and a tail, and can bark and fetch a ball
- Encapsulation = handling data with code operating on it
- Classes are like blueprints for possible states & behaviour
- class = abstract pattern (example: dog)
- object = particular representation of a class ( = Filou )
Object in Python
Everyhting is an object in Python :
-
- 5 –> class : int
- “Hello” –> class : str
- np.mean() –> class : function
# How to find out what kind of class import numpy as np a = np.array([1,2,8,5]) print(type(a)) > <class 'numpy.ndarray'>
- in case of Filou –> type(Filou) would be class Dog
Attributes and Methods
State — Attributes
import numpy as np a = np.array([1,2,8,5]) # shape attribute print(a.shape) > (4,)
- attribute — variable(s) —
obj.my_attribute
- in case of Filou –> Filou’s attributes = ‘4 legs’, ‘1 tail’
Behavior — Methods
import numpy as np a = np.array([1,2,8,5]) # reshape method a = a.reshape(2,2) print(a) > [[1 2] [8 5]]
- method — function() —
obj.my_method()
- In case of Filou –> methods are ‘barking’, ‘fetching balls’
Listing all attributes and methods
import numpy as np a = np.array([1,2,8,5]) print(dir(a)) > ['T', '__abs__', '__add__', ... ... 'transpose', 'var', 'view']
- listing of Filou –> dir(Filou) –> [‘legs’,’tail’,’bark’,’fetch_balls’ ]
help()
to see documentation on object –> provided the class documentation filled in
Class anatomy : basics
Basic Class
class Customer: # Code for class pass
class <>:
starts a class definition- code inside
class
is indented - use
pass
to create an empty class
c1 = Customer() c2 = Customer()
- Use
Classname()
to create an object of classClassName
Add methods to a class
class Customer: # creating a method ( function within the class ) def identify(self, name): print("I am customer " + name)
- method
def
(inition) = function within a class - use
self
as the 1st argument in method definition
cust = Customer() cust.identify("Marie") > I am customer Marie
- ignore
self
when calling method on an object –> in this case name is the parameter for identify()
What is self ?
- classes are templates, how to refer to data of a particular object ?
self
is a stand-in for a particular object used in class definitionself
should be the first argument of any method .- Python will take care of self when the method is called from an object
cust.identify("Marie")
is being interpreted asCustomer.identify(cust, "Marie")
Attributes in a class
- encapsulation –> bundling data with methods that operate on the data.
Customer
name should be an attribute instead of an parameter through a method
- attributes are created by assignment in methods .
Add an attribute to a class
class Customer: # set the name attribute to new_name def set_name(self, new_name): # create attribute by assigning a name self.name = new_name # <-- .name attrib. is created here cu5 = Customer() # <-- .name does not exist yet cu.set_name("John") # <-- .name is created and set to "John" print(cu.name) # <-- as the name-attribute is created, it can be used > John
New version with identify
class Customer: def set_name(self, new_name): self.name = new_name # Using the name-attribute from the object itself def identify(self): print("It is I, inspector " + self.name) cust01 = Customer() cust01.set_name("Vlad") cust01.identify() > It is I, inspector Vlad
Exercise
class MyCounter: def set_count(self, n): self.count = n def triple_num(self): self.count = self.count * 3 number1 = MyCounter() number1.set_count(3) number1.triple_num() number1.count > 15
class Employee: def set_name(self, new_name): self.name = new_name # Add set_salary() method def set_salary(self, new_salary): self.salary = new_salary # Create an object emp of class Employee emp = Employee() # Use set_name to set the name of emp to 'Korel Rossi' emp.set_name('Korel Rossi') # Set the salary of emp to 50000 emp.set_salary(50000)
class Employee: def set_name(self, new_name): self.name = new_name def set_salary(self, new_salary): self.salary = new_salary def give_raise(self, amount): self.salary = self.salary + amount # Add monthly_salary method that returns 1/12th of salary attribute def monthly_salary(self): return self.salary / 12 emp = Employee() emp.set_name('Korel Rossi') emp.set_salary(50000) # Get monthly salary of emp and assign to mon_sal mon_sal = emp.monthly_salary() # Print mon_sal print(mon_sal)
Class anatomy : The __init__
constructor
Methods and attributes
class MyClass:= # function definition in class # First Argument = self def my_method1(self, other args...): # do things def my_method2(self, my_attribute): # attribute by assignment self.my_attribute = my_attribute ...
- Methods are function definitions within a class
self
as the 1st argument- Attributes defined by assignment
- Referral to attributes in class via self.___
Constructor __init__()
class Customer: def __init__(self, name): self.name = name # <-- Create the name attribute and set it to name parameter print("The __init__ method was called") cust02 = Customer("Alfredo") # <-- __init__ is implicitly called > The __init__ method was called print(cust02.name) > Alfredo
- Add data to object when creating it ?
- Constructor
__init__()
method is called every time an object is created
class Customer: def __init__(self, name, balance): self.name = name self.balance = balance # <-- Create the name attribute balance print("The __init__ method was called") cust04 = Customer("Alfredo", 954.42) > The __init__ method was called print(cust04.name) > Alfredo print(cust04.balance) > 954.42
- with default parameters
class Customer: def __init__(self, name, balance=500): self.name = name self.balance = balance print("The __init__ method was called") cust05 = Customer("Allie") # no balance is given print(cust04.name) > Allie print(cust05.balance) > 500
Attributes in Methods
class MyClass: def my_method1(self, attr1): self.attr1 = attr1 ... def my_method2(self, attr2): self.attr2 = attr2 ... obj = MyClass() obj.my_method1(val1) # attr1 created obj.my_method2(val2) # attr2 created
Attributes in constructor
class MyClass: def __init__(self, attr1, attr2): self.attr1 = attr1 self.attr2 = attr2 ... obj = MyClass(val1, val2) # all attribs are created)
- easier to know all attributes
- attributes are created when the object is created
- more usable and maintainable code
Best Practices
- Initialize attributes in __init__()
- Naming
- CamelCase for class
- lower_snake_case for functions and attributes
- Keep self as self
- readable –> self can be renamed but less readable
- Use docstrings
class Custo: """ Test on Docstrings """ def __init__(self, name="S", balance=200): self.name = name self.balance = balance print("The __init__ method was called")
Exercise on self / __init __
# Import datetime from datetime from datetime import datetime class Employee: def __init__(self, name, salary=0): self.name = name if salary > 0: self.salary = salary else: self.salary = 0 print("Invalid salary!") # Add the hire_date attribute and set it to today's date self.hire_date = datetime.today() # ...Other methods omitted for brevity ... emp = Employee("Korel Rossi", -1000) print(emp.name) print(emp.salary) > Invalid salary! > Korel Rossi > 0
Exercise
import numpy as np class Point: def __init__(self, x=0.0, y=0.0): self.x = x self.y = y def distance(self): return np.sqrt(self.x ** 2 + self.y ** 2 ) def reflect(self, axis): if axis == 'x': self.y = - self.y elif axis == 'y': self.x = - self.x else print("Invalid axis")