Object Oriented Programming in Python
In normal life to tend to think in sequences of activities.
Procedural programming
- Code as a sequence of steps
- Great for data analysis and scripts
Object-oriented programming
- Code as interactions of objects
- Great for building framework and tools
- Maintainable and reusable code!
Objects as datastructures
Object = state + behavior
Encapsulation – bundling data with code operating on it
Classes
A Class can be seen as a blueprint for objects defining states and behaviours.
Objects in Python
- Everything in Python is an object
- Every object has a class
- Use type() to find the class
Object | Class |
5 | int |
“Hello” | str |
pd.DataFrame() | DataFrame |
np.mean | function |
… | … |
import numpy as np
a = np.array([1, 2, 3, 4,])
print(type(a))
numpy.ndarray
Attributes and methods
State <-> attributes
import numpy as np
a = np.array([1, 2, 3, 4])
# shape attribute
a.shape
(4,)
Behaviour <-> methods
import numpy as np
a = np.array([1, 2, 3, 4])
# reshape method
a.reshape(2,2)
array([[1, 2], [3, 4]])
- Use
obj.
to access attributes and methods
Object = attributes + methods
- attributes <-> variables <->
obj.my_attribute
, - method <-> function() <->
obj.my_method()
.
import numpy as np
a - np.array([1, 2, 3, 4])
dir(a) # <--- list all attributes and methods
['T', '__abs__', ... 'trace', 'transpose', 'var', 'view']
A basic class
class Customer:
# code for class goes here
pass
class <name>:
starts a class definition- code inside
class
is indented - use
pass
to create an “empty” class
c1 = Customer()
c2 = Customer()
- use
ClassName()
to creatie an object of classClassname
Adding methods to a class
class Customer:
def identify(self, name):
print("I am Customer " name)
- method definition – function definition within class
- use self as the 1st argument in method definition
cust = Customer()
cust.identify("Hylke")
I am Customer Hylke
- ignore
self
when calling method on an object
class Customer:
def identify(self, name):
print("I am Customer " + name)
cust = Customer()
cust.identify("Hylke")
What is self?
- classes are templates, how to refer data of a particular object?
self
is a stand-in for a particular object used in class definition- should be the first argument of any method
- Python will take care of
self
when methods called for an object:cust.identify("Laura")
will be interpreted asCustomer.identify(just, "Laura")
How to create attributes
- Encapsulation : bundling data with methods that operate on data
Customer
‘s’ name should be an attribute
Attributes are created by assignment (=) in methods
Adding an attribute to a class
class Customer:
# set the name attribute of an object to new_name
def set_name(self, new_name):
# Create an attribute by assigning a value
self.name = new_name # <--- will create .name when set_name is called
cust - Customer() # <--- .name doesn't exist here yet
cust.set_name("Hylke Rozema") # <--- .name is created and set to "Hylke Rozema"
print(cust.name) # <--- .name can be used
Hylke Rozema
Old version
class Customer:
# Using a parameter
def identify(self, name):
print("I am Customer" + name)
cust = Customer()
cust.identify("Mark Carr")
I am Customer Mark Carr
New version
class Customer:
def set_name(self, new_name):
self.name = new_name
# Using .name from the object it*self*
def identify(self):
print("I am Customer" + self.name)
cust = Customer()
cust.set_name("Mary Dillan")
cust.identify()
I am Customer Mary Dillan
The __init__ constructor
- Methods are function definitions within a class
self
as the first argument- Define attributes by assignment
- Refer to attributes aan class via
self.____
class MyClass:
# function definition in class
# first argument is self
def my_method1(self, other_args...):
# do things here
def my_method2(self, my_attr):
# attribute created by assignment
self.my_attr = my_attr
...
Constructor __init__() method
- Add data to object when creating it?
- Constructor
__init__()
method is called every time an object is created
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")
cust = Customer("Hylke Rozema") # <--- __init__ is implicitly called
print(cust.name)
The __init__ method was called Hylke Rozema
class Customer:
def __init__(self, name, balance): # <-- balance parameter added
self.name = name
self.balance = balance # <-- balance attribute added
print("The __init__ method was called")
cust = Customer("Hylke Rozema", 1000) # <-- __init__ is called
print(cust.name)
print(cust.balance)
The __init__ method was called Hylke Rozema 1000
Write a class from scratch
You are a Python developer writing a visualization package. For any element in a visualization, you want to be able to tell the position of the element, how far it is from other elements, and easily implement horizontal or vertical flip .
The most basic element of any visualization is a single point. In this exercise, you’ll write a class for a point on a plane from scratch.
Instructions
Define the class Point
that has:
- Two attributes,
x
andy
– the coordinates of the point on the plane; - A constructor that accepts two arguments,
x
andy
, that initialize the corresponding attributes. These arguments should have default value of0.0
; - A method
distance_to_origin()
that returns the distance from the point to the origin. The formula for that is x2+y2. - A method
reflect()
, that reflects the point with respect to the x- or y-axis:- accepts one argument
axis
, - if
axis="x"
, it sets they
(not a typo!) attribute to the negative value of they
attribute, - if
axis="y"
, it sets thex
attribute to the negative value of thex
attribute, - for any other value of
axis
, prints an error message.
- accepts one argument
Note: You can choose to use sqrt()
function from either the numpy
or the math
package, but whichever package you choose, don’t forget to import it before starting the class definition!
To check your work, you should be able to run the following code without errors:
pt = Point(x=3.0)
pt.reflect("y")
print((pt.x, pt.y))
pt.y = 4.0
print(pt.distance_to_origin())
and return the output
(-3.0,0.0) 5.0
from numpy import sqrt
# Write the class Point as outlined in the instructions
class Point:
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
def distance_to_origin(self):
return 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("Error")
pt = Point(x=3.0)
pt.reflect("y")
print((pt.x, pt.y))
pt.y = 4.0
print(pt.distance_to_origin())
Notice how you implemented distance_to_origin()
as a method instead of an attribute. Implementing it as an attribute would be less sustainable – you would have to recalculate it every time you change the values of the x
and y
attributes to make sure the object state stays current.