Skip to content
Snippets Groups Projects
Commit 8bdbd356 authored by LOUIS TYRRELL OLIPHANT's avatar LOUIS TYRRELL OLIPHANT
Browse files

lec 23 function references

parent 1c07dc13
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
## Announcements
- Spring Break next week
- Friday Lecture will be recorded and posted on Canvas -> Kaltura Gallery
%% Cell type:markdown id: tags:
## Warmup
%% Cell type:code id: tags:
``` python
# Warmup 1: Complete the recursive function.
# Write a function that prints nested lists with indenting
#
fav_stuff = [ "road bike",
["PB&J", "brownies", "spaghetti", "apples"] ,
("Brooks Ghost 13", "hoodie", "gloves"),
"macbook air",
[ "Johndee.com", "https://www.weather.gov/mkx/"],
["A", "K", ("S", "D", "K")]
]
def print_with_indenting(directory, indent_level):
for subitem in directory:
if type(subitem) == list or type(subitem) == tuple:
print("\t" * indent_level + str(type(subitem)))
# TODO make recursive call
else:
print("\t" * indent_level + str(subitem))
print_with_indenting(fav_stuff, 0)
```
%% Cell type:markdown id: tags:
[PythonTutor Link](https://pythontutor.com/visualize.html#code=def%20mystery%28a,%20b%29%3A%20%0A%23%20precondition%3A%20assume%20a%20%3E%200%20and%20b%20%3E%200%0A%20%20%20%20if%20b%20%3D%3D%201%3A%20%0A%20%20%20%20%20%20%20%20return%20a%3B%0A%20%20%20%20return%20a%20*%20mystery%28%20a,%20b%20-%201%20%29%0A%0A%23%20make%20a%20function%20call%20here%0Amystery%287,%205%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)
%% Cell type:code id: tags:
``` python
# Warmup 2: Trace Recursion by hand .... possible exam question
def mystery(a, b):
# precondition: assume a > 0 and b > 0
if b == 1:
return a;
return a * mystery( a, b - 1 )
# make a function call here
mystery(7, 5)
#Q: What would be the result if we call
#. mystery(-3, -1) ??
```
%% Cell type:markdown id: tags:
# Functions are Objects!
## Reading
- [Python for Everybody, 10.8](https://runestone.academy/ns/books/published/py4e-int/dictionaries/toctree.html)
As we have learned previously, all variables contain references to objects.
This is also true for function names, and it gives us more power as programmers.
**Learning Objectives:**
- Define a function reference and trace code that uses function references.
- Explain the default use of sorted() on lists of tuples, and dictionaries.
- Sort a list of tuples, a list of dictionaries, or a dictionary using a function as a key.
- Use a lambda expression when sorting.
%% Cell type:code id: tags:
``` python
# function references
# try this in Python Tutor
x = [1,2,3]
y = x # y holds a reference to the same object as x
def f(some_list): # f is the name of the function
return some_list[-1]
z = f(y) # z stores the result of a call to f
g = f # g holds a reference to the same function as f
# TODO: similar to calling f() and storing the result in z, but now call g and store the results in z2
print(z)
print(z2)
```
%% Cell type:markdown id: tags:
### Define a function reference and trace code that uses function references.
%% Cell type:markdown id: tags:
![function%20reference.png](attachment:function%20reference.png)
%% Cell type:markdown id: tags:
[PythonTutor Link](https://pythontutor.com/visualize.html#code=def%20hammer%28%29%3A%0A%20%20%20%20print%28%22tap%20tap%20tap%22%29%0A%20%20%20%20%0Adef%20call_n_times%28f,%20n%29%3A%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20f%28%29%0A%0Acall_n_times%28hammer,%203%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)
%% Cell type:code id: tags:
``` python
# function references can be passed as arguments ... Wow!
# first: try this in Python Tutor
def hammer():
print("tap tap tap")
# then on your own: define a function called screwdriver
def screwdriver():
pass
def call_n_times(f, n):
for i in range(n):
f()
call_n_times(hammer, 3)
# then on your own: invoke call_n_times with screwdriver and 5 as arguments
```
%% Cell type:markdown id: tags:
## Your turn!
Calculate the distance between two points using either the manhattan or euclidean distance.
%% Cell type:markdown id: tags:
![image.png](attachment:image.png)
Source: https://www.researchgate.net/figure/Example-of-Euclidean-and-Manhattan-distances-between-two-points-A-and-B-The-Euclidean_fig8_333430988
%% Cell type:markdown id: tags:
### Review: NamedTuples
We create a namedtuple `Point` with named attributes `x` and `y`. We then create lists of points we want to calculate distances between.
%% Cell type:code id: tags:
``` python
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
left_points = [
Point(0, 0),
Point(3, 3),
Point(2, 2),
# TODO: Add another point!
]
right_points = [
Point(5, 5),
Point(0, 3),
Point(2, 2),
# TODO: Add another point!
]
```
%% Cell type:markdown id: tags:
### Complete the function `calculate_distances`
%% Cell type:markdown id: tags:
We already did the math behind `manhattan_distance` and `euclidean_distance` for you!
Finish writing the `calculate_distances()` function, which should go over every point in `left_point`
and print the point, the matching point in `right_point` and the distance between them.
**Hint:** The `distance_algo` parameter should be a reference to a function that calculates the distance between two points.
Then, call calculate_distances measuring first in manhattan_distance, then euclidean_distance.
%% Cell type:code id: tags:
``` python
import math
def manhattan_distance(point1, point2):
dist_x = abs(point1.x - point2.x)
dist_y = abs(point1.y - point2.y)
return dist_x + dist_y
def euclidean_distance(point1, point2):
dist_x = (point1.x - point2.x) ** 2
dist_y = (point1.y - point2.y) ** 2
return math.sqrt(dist_x + dist_y)
# TODO: Complete this function!
def calculate_distances(distance_algo):
pass
# TODO: Calculate the distance between the points using manhattan distance
# TODO: Calculate the distance between the points using euclidean distance
```
%% Cell type:markdown id: tags:
### Explain the default use of sorted() on lists of tuples, and on dictionaries.
The `sort()` method and the `sorted()` function have two optional arguments, `key` and `reverse`. These
arguments control how the list will be sorted. The `reverse` argument defaults to `False`, but if you set
it to true then the list will be sorted in reverse order.
Take a look at the help function for `sorted()` in the cell below then try sorting the `populations`
list, first in sorted order then in reverse sorted order.
%% Cell type:code id: tags:
``` python
# print out the help for the sorted function and read about the reverse and key parameters
help(sorted)
```
%% Cell type:code id: tags:
``` python
populations = [55, 77, 33, 99, 22]
# TODO: sort populations then sort again in reverse order
```
%% Cell type:markdown id: tags:
## The `key` parameter in `sort()` and `sorted()`
The `key` parameter is a function that can be used to customize how the list will be sorted.
This is especially useful when sorting things where it is not clear what their order should be or if you
want to change the default soriting behavior. The function that should be passed to `key` takes as input
one item at a time from the list and it should return something that the `sort()` or `sorted()`
function does know how to sort, like a string or a number.
For example, take a look at the `owhockey_badgers` list below. Notice that the list contains tuples.
If you were to call `sort()` or `sorted()` on this list, the default behavior would be to sort using the first
item in each tuple, then the second, and so on.
Run the code in the cell below to see the default sorting behavior.
%% Cell type:code id: tags:
``` python
# Sorting part 1....how are lists of tuples sorted?
# olympic womens hockey badgers...first, last, age
owhockey_badgers = [ ("Hillary", "Knight", 32 ),
("Brianna", "Decker", 30),
("Amanda", "Kessel", 30),
("Alex", "Cavalenni", 30),
("Caroline", "Harvey", 19),
("Abbey", "Roquet", 24),
("Abbey", "Roques", 27)
]
# call sorted on this list of tuples
sorted(owhockey_badgers)
# what did this make? How was it sorted?
```
%% Cell type:markdown id: tags:
If you want to have the list sorted by the age of each player, then you need to tell
the sorting algorithm which item to sort by. You can create a function that takes
as input a single item from the list and returns the value that you want to be used
when sorting. In the cell below three such functions are created to return the
item from the tuple that you want to use when sorting the list.
%% Cell type:code id: tags:
``` python
# sorting part 2: define a function that returns a value from a tuple
def select0(some_tuple): # function must have exactly one parameter
return some_tuple[0]
def select1(some_tuple):
return some_tuple[1]
def select2(some_tuple):
return some_tuple[2]
# TODO: Test these functions on the tuple ("Mike", "Louis", "Cole")
test_tuple = ("Mike", "Louis", "Cole")
```
%% Cell type:markdown id: tags:
Try using each of these select functions as the key when sorting the `owhockey_badgers` list.
%% Cell type:code id: tags:
``` python
# call sorted using the 'key' argument
# sort and sorted can take a parameter named key
# key is a reference to a function!
# TODO: Try sorting by first name, last name, and age
sorted(owhockey_badgers, key=...)
```
%% Cell type:markdown id: tags:
The default behavior when sorting a list of dictionaries, is to fail. Python does not
know how to sort dictionaries. In this case you must have a `key` function that can extract
the right value so Python will know how to sort. Try sorting without passing a key function
to sort the list of dictionaries then write a function to get the `age` key from the dictionary
and use that when sorting.
%% Cell type:code id: tags:
``` python
people = [
{'first':'Louis', 'last':'Oliphant', 'age':53},
{'first':'Becky', 'last':'Brown', 'age':52},
{'first':'Sam', 'last':'Luna-Nelson', 'age':26},
{'first':'Sally', 'last':'Fields', 'age':47}
]
##TODO: first try sorting without passing a function for the key parameter
sorted(people)
```
%% Cell type:code id: tags:
``` python
##TODO: finish the get_age() function that takes a single dictionary and returns
## the age key from the dictionary.
def get_age(d):
#return the 'age' key
pass
##TODO: use the get_age function as the key and try sorting again
sorted(people, key=...)
```
%% Cell type:markdown id: tags:
### Using `lambda` functions
- `lambda` functions are a way to abstract a function reference
- lambdas are simple functions with:
- multiple possible parameters
- single **expression** line as the function body
- lambdas are useful abstractions for:
- mathematical functions
- lookup operations
- lambdas are often associated with a collection of values within a list
- Syntax:
```python
lambda parameters: expression
```
Here are some examples of creating a function using this lambda notation. Note that the variable holds the *function*. Below each creation of the function is a call to the function.
```python
f = lambda a : a + 10
print(f(1))
```
```
11
```
```python
f2 = lambda a, b : a * b
print(f2(3,4))
```
```
12
```
Let's use a lambda function to sort the `owhockey_badgers` tuple by the age (i.e. the last field).
%% Cell type:code id: tags:
``` python
sorted(owhockey_badgers, key = lambda each_tuple : each_tuple[-1])
# read the lambda as: my no-name function has each_tuple from owhockey_badgers as a parameter
# and returns each_tuple[-1] (the last element)
# the variable 'each_tuple' is like a function parameter
```
%% Cell type:code id: tags:
``` python
# TODO: sort the people list of dictionaries by the last name
sorted(people, key = ...)
```
%% Cell type:code id: tags:
``` python
# TODO: Sort the people list of dictionaries by the LENGTH of their last name
sorted(people, key = ...)
```
%% Cell type:markdown id: tags:
### This is all great, but what I'd really like to do is to sort dictionaries!
From Python 3.7 onwards, the standard dict type maintains insertion order by default ([see here](https://mail.python.org/pipermail/python-dev/2017-December/151283.html)), so a dictionary could be ordered, but what happens if you try and sort a dictionary?
%% Cell type:code id: tags:
``` python
menu = { 'pie': 3.95,
'ala mode':1.50,
'donut': 1.25,
'cookie': 0.79,
'milk':1.65,
'loaf': 5.99,
'hot dog': 4.99}
# sorted (dict) returns a list of the keys sorted
sorted(menu)
```
%% Cell type:code id: tags:
``` python
# but we can make progress on this by using the .items() method
menu.items()
```
%% Cell type:code id: tags:
``` python
# that looks like a list of tuples!
# let's sort menu.items() the same way we sorted a list of tuples
sorted(menu.items(), key = lambda t : t[0] ) # set the sorting key to a lambda expression
```
%% Cell type:code id: tags:
``` python
# now let's turn this list of tuples into a dict
dict(sorted(menu.items(), key = lambda t : t[0]))
```
%% Cell type:code id: tags:
``` python
# can you change the previous code to sort by price?
```
%% Cell type:markdown id: tags:
## After Lecture
%% Cell type:code id: tags:
``` python
# Practice sorting a list of tuples
# Practice sorting a list of dictionaries
# Practice sorting a dictionary by keys
# Practice sorting a dictionary by values
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment