4. Functions and Classes#
Let us learn how to define your own functions, and further organize them into a class for neatness and extensibility.
References: Python Tutorial (https://docs.python.org/3/tutorial/)
Section 4.7-4.8: Functions
Chapter 6: Modules
Chapter 9: Classes
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
Defining functions#
If you find yourself running the same codes again and again with different inputs, it is time to define them as a function.
Here is a simple example:
def square(x):
"""Compute x*x"""
# result returned
return x*x
square(3)
9
a = np.array([1, 2, 3])
# input `x` can be anything for which `x*x` is valid
square(a)
array([1, 4, 9])
The line encosed by “”” “”” is called a Docstring, which is shown by help( )
command.
help(square)
Help on function square in module __main__:
square(x)
Compute x*x
square?
A function does not need to return anything.
def print_square(x):
"""Print x*x"""
print(x*x)
# the end of indentation is the end of definition
print_square(a)
[1 4 9]
A function can return multiple values.
def square_cube(x):
"""Compute x**2 and x**3"""
# return multiple values separated by comma
return x**2, x**3
# results can be assigned to variables separated by comma
b, c = square_cube(a)
print(b, c)
[1 4 9] [ 1 8 27]
square_cube(3)
(9, 27)
Arguments and local variables#
A function can take single, multiple, or no arguments (inputs).
An argumet can be required, or optional with a default value.
An argument can be specified by the position, or a keyword.
def norm(x, p=2):
"""Give the L^p norm of a vector."""
y = abs(x) ** p
return np.sum(y) ** (1/p)
a = np.array([1, 2, -2])
norm(a) # default p=2
3.0
norm(a, 1) # specify by position
5.0
norm(p=1, x=a) # specify by the keywords, in any oder
5.0
Local and global variables#
Arguments and variables assigned in a function are registered in a local namespace.
y = 0 # global variable
norm(a) # this uses `y` as local variable, y=[1, 4, 9]
print(y) # the global variable `y` is not affected
0
Any global variables can be referenced within a function.
a = 1 # global variable
def add_a(x):
"""Add x and a."""
return a + x
print(add_a(1)) # 1 + 1
a = 2
print(add_a(1)) # 1 + 2
2
3
To modify a global variable from inside a function, it have to be declaired as global
.
a = 1
def addto_a(x):
"""Add x into a."""
global a
a = a + x # add x to a
addto_a(1) # a = a + 1
print(a)
addto_a(1) # a = a + 1
print(a)
2
3
You can modify an argument in a function.
def double(x):
"""Double x"""
x = 2 * x
return x
double(1)
2
Scripts, modules, and packages#
Before Jupyter (iPython) notebook was created, to reuse any code, you had to store it in a text file, with .py
extension by convention. This is called a script.
%cat haisai.py
print('Haisai!')
The standard way of running a script is to type in a terminal:
$ python haisai.py
In a Jupyter notebook, you can use %run
magic command.
%run haisai.py
Haisai!
You can edit a python script by any text editor.
In Jupyter notebook’s Files
window, you can make a new script as a Text file by New
menu, or edit an existing script by clicking the file name.
A script with function definitions is called a module.
%cat lp.py
"""L^p norm module"""
import numpy as np
def norm(x, p=2):
"""The L^p norm of a vector."""
y = abs(x) ** p
return np.sum(y) ** (1/p)
def normalize(x, p=2):
"""L^p normalization"""
return x/norm(x, p)
You can import a module and use its function by module.function()
.
import lp
help(lp)
Help on module lp:
NAME
lp - L^p norm module
FUNCTIONS
norm(x, p=2)
The L^p norm of a vector.
normalize(x, p=2)
L^p normalization
FILE
/Users/doya/OIST Dropbox/kenji doya/Python/iSciComp/lp.py
a = np.array([-3, 4])
lp.norm(a)
5.0
lp.normalize(a, 1)
array([-0.42857143, 0.57142857])
Caution: Python reads in a module only upon the first import
, as popular modules like numpy
are imorted in many modules. If you modify your module, you need to restart your kernel or call importlib.reload()
.
import importlib
importlib.reload(lp)
<module 'lp' from '/Users/doya/OIST Dropbox/kenji doya/Python/iSciComp/lp.py'>
A collection of modules are put in a directory as a package.
# see how numpy is organized
%ls $CONDA_PREFIX/lib/python3.9/site-packages/numpy
ls: /opt/anaconda3/lib/python3.9/site-packages/numpy: No such file or directory
Object Oriented Programming#
Object Oriented Programming has been advocated since 1980’s in order to avoid naming coflicts and to allow incremental software development by promoting modularity.
Examples are: SmallTalk, Objective C, C++, Java,… and Python!
Major features of OOP is:
define data structure and functions together as a Class
an instance of a class is created as an object
the data (attributes) and functions (methods) are referenced as
instance.attribute
andinstance.method()
.a new class can be created as a subclass of existing classes to inherit their attributes and methods.
Defining a basic class#
Definition of a class starts with
class ClassName(BaseClass):
and include
definition of attributes
__init__()
method called when a new instance is createddefinition of other methods
The first argument of a method specifies the instance, which is named self
by convention.
class Vector:
"""A class for vector calculation."""
default_p = 2
def __init__(self, arr): # make a new instance
self.vector = np.array(arr) # array is registered as a vector
def norm(self, p=None):
"""Give the L^p norm of a vector."""
if p == None:
p = self.default_p
y = abs(self.vector) ** p
return np.sum(y) ** (1/p)
def normalize(self):
"""normalize the vector"""
u = self.vector/self.norm()
self.vector = u
A new instance is created by calling the class like a function.
x = Vector([0, 1, 2])
Attributes and methods are referenced by .
x.vector
array([0, 1, 2])
x.norm()
2.23606797749979
x.norm(3)
2.080083823051904
x.default_p = 1
x.norm()
3.0
x.normalize()
x.vector
array([0. , 0.33333333, 0.66666667])
# another instance
y = Vector([0, 1, 2, 3])
y.norm()
3.7416573867739413
A subclass can inherit attributes and methods of base class.
class Vector2(Vector):
"""For more vector calculation."""
def double(self):
u = 2*self.vector
self.vector = u
z = Vector2([1, 2, 3])
z.vector
array([1, 2, 3])
z.double()
z.vector
array([2, 4, 6])