Lecture 3 Python Functions and Classes¶
For longer and more complex tasks, it is important to organize your code into reuseable elements. For example, if you find yourself cutting and pasting the same or similar lines of code over and over, you probably need to define a function to encapsulate that code and make it reusable. An important principle in programming in DRY: "don't repeat yourself". Repetition is tedious and opens you up to errors. Strive for elegance and simplicity in your programs.
Functions¶
Functions are a central part of advanced python programming. Functions take some inputs ("arguments") and do something in response. Usually functions return something, but not always.
# define a function
def say_hello():
"""Return the word hello."""
return 'Hello'
# functions are also objects
type(say_hello)
function
# this doesnt call
say_hello?
Signature: say_hello() Docstring: Return the word hello. File: /var/folders/7b/6t7qqfj57bb0_ml_y_5bw86r0000gn/T/ipykernel_76551/1650374671.py Type: function
# this does
say_hello()
'Hello'
# assign the result to something
res = say_hello()
res
'Hello'
# take some arguments
def say_hello_to(name):
"""Return a greeting to `name`"""
return 'Hello ' + name
# intended usage
say_hello_to('World')
'Hello World'
say_hello_to(10)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[14], line 1 ----> 1 say_hello_to(10) Cell In[12], line 4, in say_hello_to(name) 2 def say_hello_to(name): 3 """Return a greeting to `name`""" ----> 4 return 'Hello ' + name TypeError: can only concatenate str (not "int") to str
# redefine the function
def say_hello_to(name):
"""Return a greeting to `name`"""
return 'Hello ' + str(name)
say_hello_to(10)
'Hello 10'
# take an optional keyword argument
def say_hello_language(name, chinese=False):
"""Say hello in multiple languages."""
if chinese:
greeting = 'Ni Hao '
else:
greeting = 'Hello '
return greeting + name
print(say_hello_language('Matt'))
print(say_hello_language('Siyi', chinese=True))
Hello Matt Ni Hao Siyi
# flexible number of arguments
def say_hello_to_everyone(*args):
return ['hello ' + str(a) for a in args]
say_hello_to_everyone('Matt', 'Siyi', 'Kerry')
['hello Matt', 'hello Siyi', 'hello Kerry']
# The function doesn't return anything, but it changes the input arguments.
def remove_last_from_list(input_list):
input_list.pop()
names = ['Matt', 'Siyi', 'Kerry']
remove_last_from_list(names)
print(names)
remove_last_from_list(names)
print(names)
['Matt', 'Siyi'] ['Matt']
We can do something similar with a pure function.
In general, pure functions are safer and more reliable.
def remove_last_from_list_pure(input_list):
new_list = input_list.copy()
new_list.pop()
return new_list
names = ['Matt', 'Siyi', 'Kerry']
new_names = remove_last_from_list_pure(names)
print(names)
print(new_names)
['Matt', 'Siyi', 'Kerry'] ['Matt', 'Siyi']
We could spend the rest of the day talking about functions, but we have to move on.
Classes¶
We have worked with many different types of python objects so far: strings, lists, dictionaries, etc. These objects have different attributes and respond in different ways to the built-in functions (len
, etc.)
Python is an object oriented programming language.
Almost everything in Python is an object, with its properties and methods.
How can we make our own, custom objects? Answer: by defining classes.
A class to represent a Student¶
# Create a class named Student with a name.
class Student:
name = 'Matt'
print(Student.name)
Matt
The __init__() function¶
All classes have a function called __init__(), which is always executed when the class is being initiated.
class Student:
def __init__(self, name):
self.name = name
s1 = Student('Matt')
s1
<__main__.Student at 0x111dfa250>
Our class only has a single attribute so far:
s1.name
'Matt'
Let's add more, along with some input validation:
class Student:
def __init__(self, name, age, major):
self.name = name.upper()
self.major = major
self.age = age
s1 = Student('Matt', 22, 'Environmental Science')
s1.major
'Environmental Science'
s1.name
'MATT'
s1.age
22
class Student:
def __init__(self, name, age, major):
self.name = name.upper()
self.major = major
if age<0:
raise ValueError(f'Invalid age {age}')
self.age = age
s1 = Student('Matt', -46, 'Environmental Science')
s1
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[62], line 1 ----> 1 s1 = Student('Matt', -46, 'Environmental Science') 2 s1 Cell In[61], line 8, in Student.__init__(self, name, age, major) 5 self.major = major 7 if age<0: ----> 8 raise ValueError(f'Invalid age {age}') 9 self.age = age ValueError: Invalid age -46
s1 = Student('Matt', 22, 'Environmental Science')
s1
<__main__.Student at 0x1123fe6d0>
Now let's add a custom method:¶
class Student:
def __init__(self, name, age, major):
self.name = name.upper()
self.major = major
if age<0:
raise ValueError(f'Invalid age {age}')
self.age = age
def is_ES(self):
return self.major == 'Environmental Science'
s1 = Student('Matt', 22, 'Environmental Science')
s1.is_ES()
True
s2 = Student('Siyi', 25, 'Ecology')
s2.is_ES()
False
Modify Object Properties¶
s1.age = 40
s1.age
40
del(s1.age)
s1.age
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[42], line 1 ----> 1 s1.age AttributeError: 'Student' object has no attribute 'age'
s2.age
25
s1.is_ES()
True