#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Felix Muzny 10/18/2022 DS 2000 Lecture 12 - functions and optional parameters, functions and functions as parameters, if __name__ == __main__, dictionaries Logistics: - Homework 5 is due this Friday 10/21 @ 9pm - Quiz 6 is available - remote attendance (https://bit.ly/remote-ds2000-muzny) Three ways to participate (please do one of these!) 1) via the PollEverywhere website: https://pollev.com/muzny 2) via text: text "muzny" to the number 22333 to join the session 3) via Poll Everywhere app (available for iOS or Android) Where are you on HW 5? A. Haven't read the write-up B. Started read_data <--- Goal: finish this function by the end of today (10/18) C. Started lookup_planet D. Still working on most_similar/visualize E. All done Warm-up 1 - --- Write two different good, working function calls for the given generate_list function. """ import random # this example has no positinal parameters def generate_list(lower = 1, upper = 9): """ Generate a list of length 10 filled with random ints from a lower bound to an upper bound (1 - 9 by default). Parameters ---------- lower : int, optional Inclusive lower bound of integers to generate. The default is 1. upper : int, optional Inclusive upper bound of integers to generate. The default is 9. Returns ------- new_ls : list Generated list of ints. """ new_ls = [] # generate 10 numbers for i in range(10): new_ls.append(random.randint(lower, upper)) return new_ls # example passing in both parameters ls2 = generate_list(lower = 2, upper = 4) print(ls2) # we don't need to save the return value # if we just want to print it print(generate_list(lower = 2, upper = 4)) # default behavior print(generate_list()) # we can give just one of them print(generate_list(upper = 100)) # if we don't assign names, python assigns in order print(generate_list(-10, 10)) # if we specify names, we can give them in opposite order if we want print(generate_list(lower = 6, upper = 1)) print() """ Warm-up 2 - passing functions as parameters --- Write down two good, working function calls for the given list_info function and one bad, non-working function call """ def list_info(ls, func): print(func(ls)) numbers = [1, 2, 5, 6, 17] data = [[1, 12], [23, 24], [35, 36]] # Good function calls list_info(numbers, sum) list_info(numbers, len) list_info(data, len) # Non-working function calls # list_info(data, sum) # can't sum() a list of lists # list_info(numbers, sum()) # when we pass functions, no () """ Optional parameters --- Examples with python built-in functions. We always list positional (non-optional) parameters first. print("Here", "are", "words", sep="what to put in between") plt.plot(xs, ys, "o", color="red") Writing our own functions with an optional parameter --- General syntax: def function_name(positional, parameters, first, then = optional): # code Call the function as normal, but specify the values of any optional parameters that you send to the function. If a function takes both positional and optional parameters, they will be assigned in order if no optional names are specified. """ def person_info(name, likes_mushrooms = False, age = 10): print("Name", name) print("Likes mushrooms?", likes_mushrooms) print("Age", age) # example function calls person_info("Felix", True, age = 31) # doesn't work because positional is after our optional parameter # person_info(likes_mushrooms = True, age = 31, "Felix") """ Passing functions as parameters --- Using our function that we wrote from before (convert_int), write a new version that will convert everything from a list into a string. """ def convert_mutate(ls, cast = int): """ Converts all values in given list according to the given function. Changes to ints by default. Mutates the given list. Parameters ---------- ls : list list of values to be converted. cast : function, optional Function to be applied to every element in the list. The default is int. Returns ------- None. """ for i in range(len(ls)): ls[i] = cast(ls[i]) # return None print() print("convert_mutate examples") my_floats = [43.2, 35.4, 1123.123] words = "This is a sentence".split() numbers = "1 12 12 3 12 57".split() # this function mutates the given list convert_mutate(my_floats) print(my_floats) # convert back to floats by specifying the cast function convert_mutate(my_floats, cast = float) print(my_floats) # example casting strings to ints print(numbers) convert_mutate(numbers) print(numbers) print() # this would print out None print(convert_mutate(numbers, cast = str)) print(numbers) print() """ Writing our own modules & if __name__ == "__main__": --- We've written a number of quite useful functions that are rather general purpose. For instance: in earnings_lec10.py, we had: (https://course.ccs.neu.edu/ds2000/felix_lectures/earnings_lec10.py) - read_data - get_column From lecture on Friday, we had a convert list function. These are all highly useful, very general data processing functions that I might not want to re-write every time I want to use them. Solution: write our own module! 1) Write a .py file with the functions you want 2) put that .py file in the same folder as the code you're working on 3) Rejoice We want to protect the call to main() in our module with the test: if __name__ == "__main__": main() """ # no module named data utils # check for typos/location print("right before importing") import data_utils print("after importing") print() print("in lec12 starter file examples") data = data_utils.read_data("boston_earnings.csv") # list slicing, get the first 3 elements # ls[start:end] print(data[:3]) """ Dictionaries --- A dictionary is a *data structure*, similarly to lists. A dictionary links a *key* to a *value*. Examples: student emails -> graduation year words -> definitions words -> counts of how often they occur names -> ages Keys: any immutable data type (ints, floats, strings, booleans) keys are *unique* (no key can occur more than once) Values: any data type values are not unique """ # Dictionary examples # Creating a dictionary # an empty dictionary print() print("dict examples") ages = {} # a dictionary w/ some starting values ages = {"Felix": 31, "Dylan": 33} print(ages) # number of key-value pairs print(len(ages)) # adding a key/value pair to a dictionary ages["Donald Duck"] = 5 print(ages) # number of key-value pairs print(len(ages)) # updating a key/value pair in a dictionary ages["Felix"] = 32 ages["Felix"] = ages["Felix"] + 1 print(ages) # number of key-value pairs print(len(ages)) # what if I look up a key/value that doesn't exist? # KeyError # print(ages["Lizzo"]) # test for a key in a dict if "Lizzo" in ages: print(ages["Lizzo"]) else: print("oh no!") print() # iterating through all values in a dictionary for key in ages: print(key) # key print(ages[key]) # value it is linked to print() for key, value in ages.items(): # key = next key in the dict # value = ages[key] print(key) # key print(value) # value it is linked to print() # Brainteaser for Friday! """ How is a list similar to a dictionary? --- """ # if time: create a dictionary of counts def count_words(text): """ Count each word in a given string Parameters ---------- text : str words separated by whitespace. Returns ------- dict of word counts. """ print("ro be implemented") test1 = "This is a sentence" test2 = "This is a longer test that has a second clause" """ Next time: - writing a larger program with dictionaries """