<jupyter_start><jupyter_text>Lesson 2.1 Functions in Python I This lesson introduces functions in python and provides several examples.  Warm-up and Exercises[Replit Coding Warm-up and Exercises](https://repl.it/@careybaldwin/BAP-21-Exercises?language=python3&folderId=20cc2382-84ed-44e1-ade0-f27dbd48a562)  Warm-up 1Write a program to convert degrees Celcius into degrees Farenheit, using the relationship ```F=(9/5*C)+32```<jupyter_code># code here:<jupyter_output><empty_output><jupyter_text>Warm-up 2Write a program that accepts these inputs:```apr (float),years (int), principal (float),```and prints the principal value after the time period has passed.  Use the relationship: principal=principal*(1+apr).  We need a for loop to repeat the process for each year.<jupyter_code># Code here:<jupyter_output><empty_output><jupyter_text>References[A Practical Introduction to Python Programming](https://www.brianheinold.net/python/A_Practical_Introduction_to_Python_Programming_Heinold.pdf)[Markdown Syntax](https://help.github.com/en/articles/basic-writing-and-formatting-syntaxquoting-code)  Objectives- Be able to build basic functions in python and understand the syntax- Understand the return statement and know how to use it- Be able to import modules and individual functions from modules- Be able to use functions within other functions- Learn to use default arguments and understand ordering- Understand the difference between local and Global Variables  Videos- [intro to functions](https://youtu.be/-fBkBVo2iTc)- [return statements in functions](https://youtu.be/6u_d4nL3xwI)- [importing functions](https://youtu.be/xi7VYG4BN6s)- [default arguments](https://youtu.be/bUAaZFG7uQk)  2.1 Functions in PythonWe have seen many functions that have been built into python, such as print(), find() etc.  Functions allow us to make our code reusable.  It is also helpful to use functions to break up program so that it is easier to read and maintain.   2.1.1 Basics When we build our own function, it is defined with the def statement, then we give the function a name and end the statement with a colon.```def function_name():```When we give a function a name, we are adding that name to the namespace of the program we are building.  The namespace is the collection of recognized identifiers. A function can be as basic as printing something.  The indentation after the colon shows that what follows is part of the say_hello function.<jupyter_code>def say_hello():
    print("Hello, how are you?")<jupyter_output><empty_output><jupyter_text>When we want to use the function we “call” or “invoke” the function like so:<jupyter_code>say_hello()<jupyter_output><empty_output><jupyter_text>2.1.2 ArgumentsA function like this has no arguments specified (nothing in the parentheses), but we can write a function in such a way to accept one or more arguments.  Think of arguments as changeable parts of the function. They allow us to customize our result.  We give arguments a name when building the function and allow the user to specify a value when calling the function.  The name of an argument is called a "parameter".  You will see the terms "argument" and "parameter used interchangeably".<jupyter_code>def say_hello(name):
    print("Hello", name,"how are you?")<jupyter_output><empty_output><jupyter_text>Call the function:<jupyter_code>say_hello('Howard')<jupyter_output><empty_output><jupyter_text>2.1.3 Returning ValuesThe return statement can be used to send the result of the function’s calculations back to the caller.  We see this in functions that perform calculations.  It is often advantageous to have a the value returned by a function accessible throughout the program.  A function to convert a temperature from celsius to Fahrenheit.<jupyter_code>def convert(temp):
  return temp*9/5+32

type(convert(20))
def convert(temp):
  print(temp*9/5+32)

type(convert(20))<jupyter_output>68.0<jupyter_text>Because we returned a value, we can reference the function and combine it with other operations like so.<jupyter_code>convert(20)+10<jupyter_output><empty_output><jupyter_text>If no return statement, python returns None<jupyter_code>def convert(temp):
    # missing return statement
    temp*9/5+32
    
print(convert(20))<jupyter_output><empty_output><jupyter_text>We can return multiple values form a function.  Write a function that returns the first and last character of a string.<jupyter_code>def first_last(str):
    return str[0],str[-1]

first_last('abcdefg')<jupyter_output><empty_output><jupyter_text>Write a function that add up the digits of a three digit integer and returns the result.<jupyter_code>def add_digits(num):
    string=str(num)
    sum=int(string[0])+int(string[1])+int(string[2])
    print("adding the digits of", num, "yields", sum)
    return sum
   
add_digits(123)<jupyter_output><empty_output><jupyter_text>Exercise 1Write a function that takes in a name and hometown argument (both strings) and prints a statement that says "Hello, my name is (name) and I am from (hometown)"<jupyter_code># code here:<jupyter_output><empty_output><jupyter_text>Exercise 2Write a function that accepts a start_price and an end_price and returns the percent increase using the relationship ```(end_price-start_price)/start_price*100```<jupyter_code># code here:<jupyter_output><empty_output><jupyter_text>2.1.4 Importing FunctionsImporting Functions: Python has a multitude of functions available for import. Here are some ways we can do this with the math module which will give us access to a collection of functions.<jupyter_code># import the math module into our program
import math 
# try the sqrt function
math.sqrt(81) # we need to provide a reference to the module (disambiguate)
# import the math module into our program but give it the name 'm' (m is now an alias for math)
import math as m
# this makes it a little easier to reference the math module
m.sqrt(81)
# import the sqrt function into our program (this only pulls in the one function)
from math import sqrt
# We do not need to reference the module that holds this function
sqrt(81)
import sqrt from math<jupyter_output><empty_output><jupyter_text>2.1.5 Combining FunctionsWe can use imported or user-defined functions within other functions.  This is very useful for breaking down your program into manageable pieces.<jupyter_code># import a module to calculate some statistical values
from statistics import mean, median, mode<jupyter_output><empty_output><jupyter_text>Write a function that returns measures of central tendency for a list of data values.<jupyter_code>def summary(num_list):
  from statistics import mean, median, mode
  return mean(num_list), median(num_list), mode(num_list)

print("Mean, Median, Mode: ", summary([2,5,3,3,3,16,7,18]))<jupyter_output>Mean, Median, Mode:  (7.125, 4.0, 3)<jupyter_text>2.1.6 Default Arguments You can specify a default value for an argument. This makes it optional, and if the caller decides not to use it, then it takes the default value. Here is an example: The default number is specified as 123.  If the user fails to input a number to the function, the number 123 will be used.<jupyter_code>def add_digits(num=123):
    string=str(num)
    sum=int(string[0])+int(string[1])+int(string[2])
    print("adding the digits of", num, "yields", sum)
    return sum
   
add_digits(345)<jupyter_output>adding the digits of 345 yields 12<jupyter_text>Another example<jupyter_code>def divisible(dividend=45, divisor=5):
    if dividend%divisor ==0:
        return True
    else:
        return False
    
divisible(36,5)<jupyter_output><empty_output><jupyter_text>Ordering ArgumentsWe either have to input the values in the correct order or reference them by name when calling the function.<jupyter_code># specify values in order
print(divisible(36,6))

# name the values when inputting
print(divisible(divisor=6, dividend=36))

# this one won't work as intended
print(divisible(6,36))<jupyter_output>True
True
False<jupyter_text>Exercise 3Write a function that calculates the value of the principal invested at a given percentage rate after a specified time frame.  The function should take in three arguments: principal (float), apr (float), time (int).  Use the relationship principal=principal*(1+apr).  (hint: we will need to use a for loop to repeat the process each year).<jupyter_code># code here<jupyter_output><empty_output><jupyter_text>2.1.7 Local and Global VariablesLet’s say we have two functions like the ones below that each use a variable i:<jupyter_code>def func1():
    for i in range(10):
        print(i)

print(func1())

def func2():
    i=100
    func1()
    print(i)

print(func2())<jupyter_output><empty_output><jupyter_text>A problem that could arise here is that when we call func1, we might mess up the value of i in func2. In a large program it would be a nightmare trying to make sure that we don’t repeat variable names in different functions, and, fortunately, we don’t have to worry about this. When a variable is defined inside a function, it is local to that function, which means it essentially does not exist outside that function. This way each function can define its own variables and not have to worry about if those variable names are used in other functions. Global variables On the other hand, sometimes you actually do want the same variable to be available to multiple functions. Such a variable is called a global variable. You have to be careful using global variables, especially in larger programs, but a few global variables used judiciously are fine in smaller programs. Here is a short example:<jupyter_code>def reset():
    global time_left
    time_left = 0

def print_time():
    print(time_left) 
    time_left=30<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07679688301035578, 'fraction_numerical': 0.01804572951912232, 'mean_word_length': 5.498334443704197, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 85, 'https://': 7, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '123888', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 2933, 'n_tokens_neox': 2802, 'n_words': 1172, 'path': 'Copy_of_BAP_2_1_Functions_I.ipynb', 'repo_name': 'OG27/data', 'size': '9753'}