IACS Computes! High School summer camp
Day 1
Day 2
Day 3
Day 4
Day 5
Day 6
Day 7
Day 8
Day 9
Write some code that deletes all consonants from the word omnipotent.
vowels = ["a", "o", "i", "u", "e"]
word = "omnipotent"
new_string = ""
for l in word:
if l in vowels:
new_string += l
print(new_string)
oioe
Now, write some code that deletes all vowels from the word longitude.
word = "longitude"
new_string = ""
for l in word:
if l not in vowels:
new_string += l
print(new_string)
lngtd
As you may have spotted, the above two blocks of code looked more than a little similar.
To avoid repeating blocks of similar code, we can create functions.
Up until now, we’ve simply been writing code in cells of our notebooks and executing them. This is great if you want to perhaps change one thing and run a sequence of lines of code again with that one thing changed. But what if we want to run the same cell a many times? Or what if we want one cell to be able to run the code that’s in another cell? Functions allow us to do these things and really help with the organization (and therefore readability) of our code.
We’ve been using several built-in functions up to now, such as print
, range
, lower
, but now it’s time for us to write our own functions!
Let’s begin by writing a function that prints a string a given number of times. To define a function in Python, we use the keyword def
(short for define).
def print_string(my_string, n):
for i in range(n):
print(my_string)
def another_print_string(my_string, n):
print((my_string + "\n") * n)
Questions: What will be the difference in the behavior of these two functions? How would we make them both behave the same?
To run the function, we just call it with the necessary arguments.
print_string("Hello world!", 4)
another_print_string("Hello!", 3)
Hello world!
Hello world!
Hello world!
Hello world!
Hello!
Hello!
Hello!
Note: we can use variable names as the arguments of the functions.
strings_to_use = ["Hello!", "How are you?"]
for string in strings_to_use:
print_string(string, 3)
Hello!
Hello!
Hello!
How are you?
How are you?
How are you?
We can also define functions without any arguments. For example:
def complain():
print("I used to be an adventurer like you, then I took an arrow in the knee.")
def walk_around():
print("I guess I will just go to the market...")
complain()
I used to be an adventurer like you, then I took an arrow in the knee.
You can call your previously defined functions from within new functions:
def dialogue(player_coming):
if player_coming == True:
complain()
elif player_coming == False:
walk_around()
dialogue(True)
I used to be an adventurer like you, then I took an arrow in the knee.
Let’s recall our ealier example. We used variables to solve it like this:
from math import sqrt
a = 2
b = 1
c = -1
x1 = (-b + sqrt(b**2 - 4 * a * c)) / (2 * a)
x2 = (-b - sqrt(b**2 - 4 * a * c)) / (2 * a)
print(x1, x2)
0.5 -1.0
If we wish to change a
, b
and/or c
, we could just change the variables and re-run the cell again. However, a better way would be to define a function to do this instead:
def quadratic_solver(a, b, c):
x1 = (-b + sqrt(b**2 - 4 * a * c)) / (2 * a)
x2 = (-b - sqrt(b**2 - 4 * a * c)) / (2 * a)
print(x1, x2)
quadratic_solver(2, 1, -1)
quadratic_solver(1, -3, 0)
0.5 -1.0
3.0 0.0
Here’s another example of a function. This one takes a birth year, month, and day, and prints out the persons age:
def age(birth_year, birth_month, birth_day):
today = 16
this_month = 8
this_year = 2019
if (birth_month > this_month) or ((birth_month == this_month) and (birth_day >= today)):
print(this_year - birth_year - 1)
else:
print(this_year - birth_year)
age(2012, 1, 7)
7
Until now we’ve had our cells and functions simply print output to the screen, but what if we want to do something else with this output (e.g. store it in a list)? It’s not very useful to us if it’s only printed to the screen. This is where return statements come in. With return statements, we can actually use the result of a function in another piece of code. Here’s our age function written with a return statement:
def new_age(birth_year, birth_month, birth_day):
today = 16
this_month = 8
this_year = 2019
if (birth_month > this_month) or ((birth_month == this_month) and (birth_day >= today)):
return this_year - birth_year - 1
else:
return this_year - birth_year
Now let’s try calling it.
new_age(2012, 1, 7)
7
Thanks to the way Jupyter notebooks work, calling new_age
doesn’t look all that different from when we called age
. Although we didn’t tell it to, Jupyter has handily printed out the value returned by the new_age
function for us. However, we can start to see the advantage of return statements if we assign the output of the new_age
function to a variable:
blue_ivy = new_age(2012, 1, 7)
This time it didn’t print out the result, as assigning something to a variable does not provide any output. If we try printing our new variable however, we can see that the number returned by our function is now stored in that variable:
print(blue_ivy)
7
Let’s use our function to create a list:
ages = []
ages.append(new_age(1999,9,9))
ages.append(new_age(2000,12,1))
# Now use those variables to do something
print("Jean is", ages[0],"years old.")
print("Jacob is", ages[1],"years old.")
Jean is 19 years old.
Jacob is 18 years old.
What will happen if we use the age
function instead of new_age
?
Let’s use our newly learned function writing skills to build a chatbot. This chatbot is not going to be all that smart (for now), but will return some ‘opinions’ based on the inputs it is given.
def chatbot(name, animal, state):
print("Hello, " + name + "!\n")
print("Oh, I see you like " + animal+ "s...")
if animal in ["cat", "dog"]:
print("That choice is pretty usual.\n")
elif animal in ["spider", "salamander"]:
print("I didn't expect that!\n")
else:
print("How is it even possible?!\n")
print("Wow, you're from " + state + ".")
if state in ["Connecticut", "Massachusetts", "Rhode Island", "New Hampshire", "Maine", "Vermont",
"New York", "Michigan", "Wisconsin", "Iowa", "Minnesota", "South Dakota", "North Dakota",
"Montana", "Idaho", "Oregon", "Washington"]:
print("Pretty cold there, right?")
else:
print("That's good.")
return name + " from " + state + " likes " + animal + "s!"
jimmy = chatbot("Jimmy", "cat", "Oregon")
Hello, Jimmy!
Oh, I see you like cats...
That choice is pretty usual.
Wow, you're from Oregon.
Pretty cold there, right?
print(jimmy)
Jimmy from Oregon likes cats!
What happens if we put return
before some other code in our function?
def bad_chatbot(name, animal, state):
return name + " from " + state + " likes " + animal + "s!"
print("Hello, " + name + "!\n")
alena = bad_chatbot("Alena", "cat", "New York")
The function exits and returns the result as soon as it hits the return statement, so it never gets to the last line in our function.
Question: how can we have two return
statements within the same function definition?
def weird_thing(n):
if n < 0:
return "Hey"
else:
return "Hi"
weird_thing(5)
'Hi'
Write a function that takes an integer n
and prints the first n
positive integers (i.e. integers from 1 to n
). So if n = 5
, it would print
1
2
3
4
5
def num(n):
for i in range(1, n+1):
print(i)
num(6)
1
2
3
4
5
6
Write a function that takes a string and counts how many vowels (a,e,i,o,u) are there in the string. Print the result.
def count_vowels(a_string):
vowels = ['a', 'e', 'i', 'o', 'u']
num_vowels = 0
for vowel in vowels:
num_vowels += a_string.count(vowel)
print(f"There are {num_vowels} in this string")
count_vowels("this is a string")
There are 4 in this string
count_vowels("this is another string")
There are 6 in this string
Now, remember the first thing we did today. Write a function that takes two arguments: string
and t
, and returns the version of the string without consonants if the value of t
is c
, and removes vowels if the value of the t
is v
. Otherwise it returns False
.
def remove_letters(string, t):
vowels = ['a', 'e', 'i', 'o', 'u']
newstring = ""
if t == 'c':
for letter in string:
if letter in vowels:
newstring += letter
elif t == 'v':
for letter in string:
if letter not in vowels:
newstring += letter
else:
return False
return newstring
var = remove_letters("this is a string", "a")
print(var)
False
type(var)
bool
Write a function that takes 4 arguments: name
, year
, month
, and day
. It then greets the user and happliy tells them how old they are, before returning ther age.
def age(name, year, month, day):
print(f"Hello {name}!")
today = 16
this_month = 8
this_year = 2019
age = 0
if (month > this_month) or ((month == this_month) and (day >= today)):
age = this_year - year - 1
else:
age = this_year - year
print(f"Congratulations! You are {age} years old!")
return age
billy = age("Billy", 2001, 10, 23)
Hello Billy!
Congratulations! You are 17 years old!
billy
17
Write a function that takes a list of strings and concatenates every string to the strings in front of it in the list. For example, the list [“cat”, “dog”, “fox”,”pig”] would return [“cat”,”catdog”,”catdogfox”,”catdogfoxpig”]
def concat(str_list):
new_list = []
for i, word in enumerate(str_list):
new_string = "".join(str_list[:i+1])
new_list.append(new_string)
return new_list
concat(["cat", "dog", "fox","pig"] )
['cat', 'catdog', 'catdogfox', 'catdogfoxpig']
Write a function that takes an integer and returns True
if that number is prime, and False
if it isn’t. Recall that a positive integer greater than 1 is prime if it can only be divided by 1 and itself.
def prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
prime(13)
True
Write a function that takes an integer and returns a list of all the factors of the input number.
def factors(n):
my_list = []
for i in range(1, n+1):
if n % i == 0:
my_list.append(i)
return my_list
factors(15)
[1, 3, 5, 15]
Create a function that analyzes a password. A good password must contain a lowercase letter, an uppercase letter, a number, and one of the following characters: .
, ,
, !
, ?
, $
, &
, ^
, :
, ;
. Also, it must be not shorter than 9 characters.
Return True
if the password is good, and False
if it is not.
Test the following strings:
a = "string"
a.islower()
True
def analyze_password(password):
contains_lower = False
contains_upper = False
contains_number = False
contains_char = False
for letter in password:
if letter.islower():
contains_lower = True
elif letter.isupper():
contains_upper = True
elif letter.isdigit():
contains_number = True
elif letter in ['.', ',', '!', '?', '$', '&', '^', ':', ';']:
contains_char = True
if (contains_lower and contains_upper and contains_number
and contains_char and len(password) >= 9):
return True
else:
return False
print(analyze_password('password_is_strong'))
print(analyze_password('myNumberis99:'))
print(analyze_password('NuMbErSaReFuN'))
print(analyze_password('l33Tn3rd!)'))
False
True
False
True
By the way, this advice is wrong: read this article from USA today. People still tend to choose very predictable sequences of numbers, words, and special symbols. A much more secure option is to choose a longer password made up of several random words, e.g. purple tortoise silence cobras -> purpletortoisesilencecobras
.