<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Chapter 11
Step2:  This could be the purpose of a function
Step4:  If we execute the code above, we don't get any output. That's because we only told Python
Step7:  1.3.2 Calling a function from within another function
We can also define functions that call other functions, which is very helpful if we want to split our task into smaller, more manageable subtasks
Step8:  You can do the same tricks that we learnt to apply on the built-in functions, like asking for help or for a function type
Step10:  The help we get on a function will become more interesting once we learn about function inputs and outputs ;-)
1.4 Working with function input
1.4.1 Parameters and arguments
We use parameters and arguments to make a function execute a task depending on the input we provide. For instance, we can change the function above to input the name of a person and print a birthday song using this name. This results in a more generic function.
To understand how we use parameters and arguments, keep in mind the distinction between function definition and function call.
Parameter
Step11:  We can also store the name in a variable
Step12:  If we forgot to specify the name, we get an error
Step14:  Functions can have multiple parameters. We can for example multiply two numbers in a function (using the two parameters x and y) and then call the function by giving it two arguments
Step16:  1.4.2 Positional vs keyword parameters and arguments
The function definition tells Python which parameters are positional and which are keyword. As you might remember, positional means that you have to give an argument for that parameter;  keyword means that you can give an argument value, but this is not necessary because there is a default value.
So, to summarize these two notes, we distinguish between
Step17:  If we do not specify a value for a positional parameter, the function call will fail (with a very helpful error message)
Step19:  1.5 Output
Step20:  We can also print the result directly (without assigning it to a variable), which gives us the same effect as using the print statements we used before
Step22:  If we assign the result to a variable, but do not use the return statement, the function cannot return it. Instead, it returns None (as you can try out below).
This is important to realize
Step24:  Returning multiple values
Similarly as the input, a function can also return multiple values as output. We call such a collection of values a tuple (does this term sound familiar ;-)?).
Step25:  Make sure you actually save your 2 values into 2 variables, or else you end up with errors or unexpected behavior
Step27:  Saving the resulting values in different variables can be useful when you want to use them in different places in your code
Step29:  1.6 Documenting your functions with docstrings
Docstring is a string that occurs as the first statement in a function definition.
For consistency, always use 
Step32:  You can see that this docstring describes the function goal, its parameters, its outputs, and the errors it raises.
It is a good practice to write a docstring for your functions, so we will always do this! For now we will stick with  single-sentence docstrings
You can read more about this topic here, here, and here.
1.7 Debugging a function
Sometimes, it can hard to write a function that works perfectly. A common practice in programming is to check whether the function performs as you expect it to do. The assert statement is one way of debugging your function. The syntax is as follows
Step33:  If the function output is what you expect, Python will show nothing.
Step35:  However, when the actual output is different from what we expected, we got an error. Let's say we made a mistake in writing the function.
Step36:  1.8 Storing a function in a Python module
Since Python functions are nice blocks of code with a clear focus, wouldn't it be nice if we can store them in a file? By doing this, we make our code visually very appealing since we are only left with functions calls instead of function definitions.
Please open the file utils_chapter11.py (is in the same folder as the notebook you are now reading). In it, you will find three of the functions that we've shown so far in this notebook. So, how can we use those functions? We can import the function using the following syntax
Step38:  2. Variable scope
Please note
Step40:  Even when we return x, it does not exist outside of the function
Step42:  Also consider this
Step44:  In fact, this code has produced two completely unrelated x's!
So, you can not read a local variable outside of the local context. Nevertheless, it is possible to read a global variable from within a function, in a strictly read-only fashion.
Step46:  You can use two built-in functions in Python when you are unsure whether a variable is local or global. The function locals() returns a list of all local variables, and the function globals() - a list of all global variables. Note that there are many non-interesting system variables that these functions return, so in practice it is best to check for membership with the in operator. For example
Step49:  Finally, note that the local context stays local to the function, and is not shared even with other functions called within a function, for example
Step50:  We call the function setb() from the global context, and we call the function setb_again() from the context of the function setb(). The variable b in the function setb_again() is set to 3, but this does not affect the value of this variable in the function setb() which is still 2. And as we saw before, the changes in setb() do not influence the value of the global variable (b=1).
Exercises
Exercise 1
Step52:  Exercise 2
Step54:  Exercise 3
Step56:  Exercise 4
Step58:  Exercise 5
Step59:  Exercise 6
<ASSISTANT_TASK:>
Python Code:
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear Emily.")
print("Happy Birthday to you!")
Explanation: Chapter 11: Functions and scope
We use an example from this website to show you some of the basics of writing a function. 
We use some materials from this other Python course.
We have seen that Python has several built-in functions (e.g. print() or max()). But you can also create a function. A function is a reusable block of code that performs a specific task. Once you have defined a function, you can use it at any place in your Python script. You can even import a function from an external module (as we will see in the next chapter). Therefore, they are beneficial for tasks that you will perform more often. Plus, functions are a convenient way to order your code and make it more readable!
At the end of this chapter, you will be able to:
write a function
work with function inputs
understand the difference between (keyword and positional) arguments and parameters
return zero, one, or multiple values
write function docstrings
understand the scope of variables
store your function in a Python module and call it
debug your functions
If you want to learn more about these topics, you might find the following link useful:
Tutorial: Defining Functions of your Own
The docstrings main formats
PEP 287 -- reStructured Docstring Format
Introduction to assert
Now let's get started!
If you have questions about this chapter, please contact us (cltl.python.course@gmail.com).
1. Writing a function
A function is an isolated chunk of code that has a name, gets zero or more parameters, and returns a value. In general, a function will do something for you based on the input parameters you pass it, and it will typically return a result. You are not limited to using functions available in the standard library or the ones provided by external parties. You can also write your own functions!
Whenever you are writing a function, you need to think of the following things:
* What is the purpose of the function?
* How should I name the function?
* What input does the function need?
* What output should the function generate?
1.1. Why use a function?
There are several good reasons why functions are a vital component of any non-ridiculous programmer:
encapsulation: wrapping a piece of useful code into a function so that it can be used without knowledge of the specifics
generalization: making a piece of code useful in varied circumstances through parameters
manageability: Dividing a complex program up into easy-to-manage chunks
maintainability: using meaningful names to make the program better readable and understandable
reusability: a good function may be useful in multiple programs
recursion!
1.2. How to define a function
Let's say we want to sing a birthday song to Emily. Then we print the following lines:
End of explanation
def happy_birthday_to_emily(): # Function definition
    
    Print a birthday song to Emily.
    
    print("Happy Birthday to you!")
    print("Happy Birthday to you!")
    print("Happy Birthday, dear Emily.")
    print("Happy Birthday to you!")
Explanation: This could be the purpose of a function: to print the lines of a birthday song for Emily. 
Now, we define a function to do this. Here is how you define a function:
write def;
the name you would like to call your function;
a set of parentheses containing the parameter(s) of your function;
a colon;
a docstring describing what your function does;
the function definition;
ending with a return statement
Statements must be indented so that Python knows what belongs in the function and what not. Functions are only executed when you call them. It is good practice to define your functions at the top of your program or in another Python module.
We give the function a clear name, happy_birthday_to_emily, and we define the function as shown below. Note that we specify what it does in the docstring at the beginning of the function:
End of explanation
# function definition:
def happy_birthday_to_emily(): # Function definition
    
    Print a birthday song to Emily.
    
    print("Happy Birthday to you!")
    print("Happy Birthday to you!")
    print("Happy Birthday, dear Emily.")
    print("Happy Birthday to you!")
    
# function call:
print('Function call 1')
happy_birthday_to_emily()
print()
# We can call the function as many times as we want (but we define it only once)
print('Function call 2')
happy_birthday_to_emily()
print()
print('Function call 3')
happy_birthday_to_emily()
print()
# This will not call the function 
print('This is not a function call')
happy_birthday_to_emily
Explanation: If we execute the code above, we don't get any output. That's because we only told Python: "Here's a function to do this, please remember it." If we actually want Python to execute everything inside this function, we have to call it:
1.3 How to call a function
It is important to distinguish between a function definition and a function call. We illustrate this in 1.3.1. You can also call functions from within other functions. This will become useful when you split up your code into small chunks that can be combined to solve a larger problem. This is illustrated in 1.3.2. 
1.3.1) A simple function call
A function is defined once. After the definition, Python has remembered what this function does in its memory.
A function is executed/called as many times as we like. When calling a function, you should always use parenthesis.
End of explanation
def new_line():
    Print a new line.
    print()
def two_new_lines():
    Print two new lines.
    new_line()
    new_line()
print("Printing a single line...")
new_line()
print("Printing two lines...")
two_new_lines()
print("Printed two lines")
Explanation: 1.3.2 Calling a function from within another function
We can also define functions that call other functions, which is very helpful if we want to split our task into smaller, more manageable subtasks:
End of explanation
help(happy_birthday_to_emily)
type(happy_birthday_to_emily)
Explanation: You can do the same tricks that we learnt to apply on the built-in functions, like asking for help or for a function type:
End of explanation
# function definition with using the parameter `name'
def happy_birthday(name): 
    
    Print a birthday song with the "name" of the person inserted.
    
    print("Happy Birthday to you!")
    print("Happy Birthday to you!")
    print(f"Happy Birthday, dear {name}.")
    print("Happy Birthday to you!")
# function call using specifying the value of the argument
happy_birthday("James")
Explanation: The help we get on a function will become more interesting once we learn about function inputs and outputs ;-)
1.4 Working with function input
1.4.1 Parameters and arguments
We use parameters and arguments to make a function execute a task depending on the input we provide. For instance, we can change the function above to input the name of a person and print a birthday song using this name. This results in a more generic function.
To understand how we use parameters and arguments, keep in mind the distinction between function definition and function call.
Parameter: The variable name in the function definition below is a parameter. Variables used in function definitions are called parameters. 
Argument: The variable my_name in the function call below is a value for the parameter name at the time when the function is called. We refer to such variables as arguments. We use arguments so we can direct the function to do different kinds of work when we call it at different times.
End of explanation
my_name="James"
happy_birthday(my_name)
Explanation: We can also store the name in a variable:
End of explanation
happy_birthday()
Explanation: If we forgot to specify the name, we get an error:
End of explanation
def multiply(x, y):
    Multiply two numeric values.
    result = x * y
    print(result)
       
multiply(2020,5278238)
multiply(2,3)
Explanation: Functions can have multiple parameters. We can for example multiply two numbers in a function (using the two parameters x and y) and then call the function by giving it two arguments:
End of explanation
def multiply(x, y, third_number=1): # x and y are positional parameters, third_number is a keyword parameter
    Multiply two or three numbers and print the result.
    result=x*y*third_number
    print(result)
multiply(2,3) # We only specify values for the positional parameters
multiply(2,3,third_number=4) # We specify values for both the positional parameters, and the keyword parameter
Explanation: 1.4.2 Positional vs keyword parameters and arguments
The function definition tells Python which parameters are positional and which are keyword. As you might remember, positional means that you have to give an argument for that parameter;  keyword means that you can give an argument value, but this is not necessary because there is a default value.
So, to summarize these two notes, we distinguish between:
1) positional parameters: (we indicate these when defining a function, and they are compulsory when calling the function)
2) keyword parameters: (we indicate these when defining a function, but they have a default value - and are optional when calling the function)
For example, if we want to have a function that can either multiply two or three numbers, we can make the third parameter a keyword parameter with a default of 1 (remember that any number multiplied with 1 results in that number):
End of explanation
multiply(3)
Explanation: If we do not specify a value for a positional parameter, the function call will fail (with a very helpful error message):
End of explanation
def multiply(x, y):
    Multiply two numbers and return the result.
    multiplied = x * y
    return multiplied
#here we assign the returned value to variable z
result = multiply(2, 5)
print(result)
Explanation: 1.5 Output: the return statement
Functions can have a return statement. The return statement returns a value back to the caller and always ends the execution of the function. This also allows us to use the result of a function outside of that function by assigning it to a variable:
End of explanation
print(multiply(30,20))
Explanation: We can also print the result directly (without assigning it to a variable), which gives us the same effect as using the print statements we used before:
End of explanation
def multiply_no_return(x, y):
    Multiply two numbers and does not return the result.
    result = x * y
    
is_this_a_result = multiply_no_return(2,3)
print(is_this_a_result)
Explanation: If we assign the result to a variable, but do not use the return statement, the function cannot return it. Instead, it returns None (as you can try out below).
This is important to realize: even functions without a return statement do return a value, albeit a rather boring one. This value is called None (it’s a built-in name). You have seen this already with list methods - for example list.append(val) adds a value to a list, but does not return anything explicitly.
End of explanation
def calculate(x,y):
    Calculate product and sum of two numbers.
    product = x * y
    summed = x + y
    
    #we return a tuple of values
    return product, summed
# the function returned a tuple and we unpack it to var1 and var2
var1, var2 = calculate(10,5)
print("product:",var1,"sum:",var2)
Explanation: Returning multiple values
Similarly as the input, a function can also return multiple values as output. We call such a collection of values a tuple (does this term sound familiar ;-)?).
End of explanation
#this will assign `var` to a tuple:
var = calculate(10,5)
print(var)
#this will generate an error
var1, var2, var3 = calculate(10,5)
Explanation: Make sure you actually save your 2 values into 2 variables, or else you end up with errors or unexpected behavior:
End of explanation
def sum_and_diff_len_strings(string1, string2):
    
    Return the sum of and difference between the lengths of two strings.
    
    sum_strings = len(string1) + len(string2)
    diff_strings = len(string1) - len(string2)
    return sum_strings, diff_strings
sum_strings, diff_strings = sum_and_diff_len_strings("horse", "dog")
print("Sum:", sum_strings)
print("Difference:", diff_strings)
Explanation: Saving the resulting values in different variables can be useful when you want to use them in different places in your code:
End of explanation
def my_function(param1, param2):
    
    This is a reST style.
    :param param1: this is a first param
    :param param2: this is a second param
    :returns: this is a description of what is returned
    
    return 
Explanation: 1.6 Documenting your functions with docstrings
Docstring is a string that occurs as the first statement in a function definition.
For consistency, always use triple double quotes around docstrings. Triple quotes are used even though the string fits on one line. This makes it easy to expand it later.
There's no blank line either before or after the docstring.
The docstring is a phrase ending in a period. It prescribes the function or method's effect as a command ("Do this", "Return that"), not as a description; e.g., don't write "Returns the pathname ...".
In practice, there are several formats for writing docstrings, and all of them contain more information than the single sentence description we mention here. Probably the most well-known format is reStructured Text. Here is an example of a function description in reStructured Text (reST):
End of explanation
def is_even(p):
    Check whether a number is even.
    if p % 2 == 1:
        return False
    else:
        return True
Explanation: You can see that this docstring describes the function goal, its parameters, its outputs, and the errors it raises.
It is a good practice to write a docstring for your functions, so we will always do this! For now we will stick with  single-sentence docstrings
You can read more about this topic here, here, and here.
1.7 Debugging a function
Sometimes, it can hard to write a function that works perfectly. A common practice in programming is to check whether the function performs as you expect it to do. The assert statement is one way of debugging your function. The syntax is as follows:
assert code == your expected output,message to show when code does not work as you'd expected
Let's try this on our simple function.
End of explanation
input_value = 2
expected_output = True
actual_output = is_even(input_value)
assert actual_output == expected_output, f'expected {expected_output}, got {actual_output}'
Explanation: If the function output is what you expect, Python will show nothing.
End of explanation
def is_even(p):
    Check whether a number is even.
    if p % 2 == 1:
        return False
    else:
        return False
input_value = 2
expected_output = True
actual_output = is_even(input_value)
assert actual_output == expected_output, f'expected {expected_output}, got {actual_output}'
Explanation: However, when the actual output is different from what we expected, we got an error. Let's say we made a mistake in writing the function.
End of explanation
from utils_chapter11 import happy_birthday
happy_birthday('George')
from utils_chapter11 import multiply
multiply(1,2)
from utils_chapter11 import is_even
is_it_even = is_even(5)
print(is_it_even)
Explanation: 1.8 Storing a function in a Python module
Since Python functions are nice blocks of code with a clear focus, wouldn't it be nice if we can store them in a file? By doing this, we make our code visually very appealing since we are only left with functions calls instead of function definitions.
Please open the file utils_chapter11.py (is in the same folder as the notebook you are now reading). In it, you will find three of the functions that we've shown so far in this notebook. So, how can we use those functions? We can import the function using the following syntax:
from NAME OF FILE WITHOUT .PY import function name
End of explanation
def setx():
    Set the value of a variable to 1.
    x = 1
    
setx()
print(x)
Explanation: 2. Variable scope
Please note: scope is a hard concept to grasp, but we think it is important to introduce it here. We will do our best to repeat it during the course.
Any variables you declare in a function, as well as the arguments that are passed to a function will only exist within the scope of that function, i.e., inside the function itself. The following code will produce an error, because the variable x does not exist outside of the function:
End of explanation
def setx():
    Set the value of a variable to 1.
    x = 1
    return x
    
setx()
print(x)
Explanation: Even when we return x, it does not exist outside of the function:
End of explanation
x = 0
def setx():
    Set the value of a variable to 1.
    x = 1
setx()
print(x)
Explanation: Also consider this:
End of explanation
x = 1
def getx():
    Print the value of a variable x.
    print(x)
    
getx()
Explanation: In fact, this code has produced two completely unrelated x's!
So, you can not read a local variable outside of the local context. Nevertheless, it is possible to read a global variable from within a function, in a strictly read-only fashion.
End of explanation
a=3
b=2
def setb():
    Set the value of a variable b to 11.
    b=11
    c=20
    print("Is 'a' defined locally in the function:", 'a' in locals())
    print("Is 'b' defined locally in the function:", 'b' in locals())
    print("Is 'b' defined globally:", 'b' in globals())
    
setb()
print("Is 'a' defined globally:", 'a' in globals())
print("Is 'b' defined globally:", 'b' in globals())
print("Is 'c' defined globally:", 'c' in globals())
Explanation: You can use two built-in functions in Python when you are unsure whether a variable is local or global. The function locals() returns a list of all local variables, and the function globals() - a list of all global variables. Note that there are many non-interesting system variables that these functions return, so in practice it is best to check for membership with the in operator. For example:
End of explanation
def setb_again():
    Set the value of a variable to 3.
    b=3
    print("in 'setb_again' b =", b)
def setb():
    Set the value of a variable b to 2.
    b=2
    setb_again()
    print("in 'setb' b =", b)
b=1
setb()
print("global b =", b)
Explanation: Finally, note that the local context stays local to the function, and is not shared even with other functions called within a function, for example:
End of explanation
# you code here
Explanation: We call the function setb() from the global context, and we call the function setb_again() from the context of the function setb(). The variable b in the function setb_again() is set to 3, but this does not affect the value of this variable in the function setb() which is still 2. And as we saw before, the changes in setb() do not influence the value of the global variable (b=1).
Exercises
Exercise 1: 
Write a function that converts meters to centimeters and prints the resulting value.
End of explanation
# function to modify:
def multiply(x, y, third_number=1): 
    Multiply two or three numbers and print the result.
    result=x*y*third_number
    print(result)
Explanation: Exercise 2: 
Add another keyword parameter message to the multiply function, which will allow a user to print a message. The default value of this keyword parameter should be an empty string. Test this with 2 messages of your choice. Also test it without specifying a value for the keyword argument when calling a function.
End of explanation
def new_line():
    Print a new line.
    print()
    
# you code here
Explanation: Exercise 3: 
Write a function called multiple_new_lines which takes as argument an integer and prints that many newlines by calling the function newLine.
End of explanation
def happy_birthday_to_you():
    # your code here
# original function - replace the print statements by the happy_birthday_to_you() function:
def happy_birthday(name): 
    
    Print a birthday song with the "name" of the person inserted.
    
    print("Happy Birthday to you!")
    print("Happy Birthday to you!")
    print("Happy Birthday, dear " + name + ".")
    print("Happy Birthday to you!")
Explanation: Exercise 4: 
Let's refactor the happy birthday function to have no repetition. Note that previously we print "Happy birthday to you!" three times. Make another function happy_birthday_to_you() that only prints this line and call it inside the function happy_birthday(name).
End of explanation
def multiply(x, y, third_number=1): 
    Multiply two or three numbers and print the result.
    result=x*y*third_number
    
    return result
    
print(multiply(1+1,6-2))
print(multiply(multiply(4,2),multiply(2,5)))
print(len(str(multiply(10,100))))
Explanation: Exercise 5: 
Try to figure out what is going on in the following examples. How does Python deal with the order of calling functions?
End of explanation
def switch_two_values(x,y):
# your code here
    
a='orange'
b='apple'
a,b = switch_two_values(a,b) # `a` should contain "apple" after this call, and `b` should contain "orange"
print(a,b)
Explanation: Exercise 6: 
Complete this code to switch the values of two variables:
End of explanation
<END_TASK>