<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Introduction to Python
Forked from Lecture 1 of Scientific Python Lectures by J.R. Johansson
Python Program Files
Python code is usually stored in text files with the file ending in ".py"
Step1:  Jupyter Notebooks
This file - a Jupyter (IPython) notebook - does not follow the standard pattern with Python code in a text file. Instead, an IPython notebook is stored as a file in the JSON format. The advantage is that we can mix formatted text, Python code and code output. It requires the IPython notebook server to run it though, and therefore isn't a stand-alone Python program as described above. Other than that, there is no difference between the Python code that goes into a program file or an IPython notebook.
Modules
Most of the functionality in Python is provided by modules. The Python Standard Library is a large collection of modules that provides cross-platform implementations of common facilities such as access to the operating system, file I/O, string management, network communication, and much more.
References
The Python Language Reference
Step2:  This includes the whole module and makes it available for use later in the program. For example, we can do
Step3:  Alternatively, we can chose to import all symbols (functions and variables) in a module to the current namespace (so that we don't need to use the prefix "math." every time we use something from the math module
Step4:  This pattern can be very convenient, but in large programs that include many modules it is often a good idea to keep the symbols from each module in their own namespaces, by using the import math pattern. This would elminate potentially confusing problems with name space collisions.
As a third alternative, we can chose to import only a few selected symbols from a module by explicitly listing which ones we want to import instead of using the wildcard character *
Step5:  Looking at what a module contains, and its documentation
Once a module is imported, we can list the symbols it provides using the dir function
Step6:  And using the function help we can get a description of each function (almost .. not all functions have docstrings, as they are technically called, but the vast majority of functions are documented this way).
Step7:  We can also use the help function directly on modules
Step8:  Variables and types
Symbol names
Variable names in Python can contain alphanumerical characters a-z, A-Z, 0-9 and some special characters such as _. Normal variable names must start with a letter. 
By convention, variable names start with a lower-case letter, and Class names start with a capital letter. 
In addition, there are a number of Python keywords that cannot be used as variable names. These keywords are
Step9:  Although not explicitly specified, a variable does have a type associated with it. The type is derived from the value that was assigned to it.
Step10:  If we assign a new value to a variable, its type can change.
Step11:  If we try to use a variable that has not yet been defined we get an NameError
Step12:  Fundamental types
Step13:  Type utility functions
Step14:  We can also use the isinstance method for testing types of variables
Step15:  Type casting
Step16:  Operators and comparisons
Most operators and comparisons in Python work as one would expect
Step17:  Note
Step18:  Comparison operators &gt;, &lt;, &gt;= (greater or equal), &lt;= (less or equal), == equality, is identical.
Step19:  Exercise
Step20:  We can index a character in a string using []
Step21:  Heads up MATLAB and R users
Step22:  If we omit either (or both) of start or stop from [start
Step23:  We can also define the step size using the syntax [start
Step24:  This technique is called slicing. Read more about the syntax here
Step25:  Exercise
Step26:  We can use the same slicing techniques to manipulate lists as we could use on strings
Step27:  Heads up MATLAB and R users
Step28:  Elements in a list do not all have to be of the same type
Step29:  Python lists can be heterogeneous and arbitrarily nested
Step30:  Lists play a very important role in Python. For example they are used in loops and other flow control structures (discussed below). There are a number of convenient functions for generating lists of various types, for example the range function
Step31:  Adding, inserting, modifying, and removing elements from lists
Step32:  We can modify lists by assigning new values to elements in the list. In technical jargon, lists are mutable.
Step33:  Insert an element at an specific index using insert
Step34:  Remove first element with specific value using 'remove'
Step35:  Remove an element at a specific location using del
Step36:  See help(list) for more details, or read the online documentation 
Tuples
Tuples are like lists, except that they cannot be modified once created, that is they are immutable. 
In Python, tuples are created using the syntax (..., ..., ...), or even ..., ...
Step37:  We can unpack a tuple by assigning it to a comma-separated list of variables
Step38:  If we try to assign a new value to an element in a tuple we get an error
Step39:  Dictionaries
Dictionaries are also like lists, except that each element is a key-value pair. The syntax for dictionaries is {key1 
Step40:  Exercise
Step41:  For the first time, here we encounted a peculiar and unusual aspect of the Python programming language
Step42:  Loops
In Python, loops can be programmed in a number of different ways. The most common is the for loop, which is used together with iterable objects, such as lists. The basic syntax is
Step43:  The for loop iterates over the elements of the supplied list, and executes the containing block once for each element. Any kind of list can be used in the for loop. For example
Step44:  Note
Step45:  To iterate over key-value pairs of a dictionary
Step46:  Sometimes it is useful to have access to the indices of the values when iterating over a list. We can use the enumerate function for this
Step47:  List comprehensions
Step48:  while loops
Step49:  Note that the print("done") statement is not part of the while loop body because of the difference in indentation.
Exercise
Step51:  Optionally, but highly recommended, we can define a so called "docstring", which is a description of the functions purpose and behaivor. The docstring should follow directly after the function definition, before the code in the function body.
Step53:  Functions that returns a value use the return keyword
Step55:  We can return multiple values from a function using tuples (see above)
Step56:  Default argument and keyword arguments
In a definition of a function, we can give default values to the arguments the function takes
Step57:  If we don't provide a value of the debug argument when calling the the function myfunc it defaults to the value provided in the function definition
Step58:  If we explicitly list the name of the arguments in the function calls, they do not need to come in the same order as in the function definition. This is called keyword arguments, and is often very useful in functions that takes a lot of optional arguments.
Step59:  Unnamed functions (lambda function)
In Python we can also create unnamed functions, using the lambda keyword
Step60:  This technique is useful for example when we want to pass a simple function as an argument to another function, like this
Step64:  Exercise
Step65:  To create a new instance of a class
Step66:  To invoke a class method in the class instance p
Step71:  Note that calling class methods can modifiy the state of that particular class instance, but does not effect other class instances or any global variables.
That is one of the nice things about object-oriented design
Step72:  We can import the module mymodule into our Python program using import
Step73:  Use help(module) to get a summary of what the module provides
Step74:  If we make changes to the code in mymodule.py, we need to reload it using reload
Step75:  Exceptions
In Python errors are managed with a special language construct called "Exceptions". When errors occur exceptions can be raised, which interrupts the normal program flow and fallback to somewhere else in the code where the closest try-except statement is defined.
To generate an exception we can use the raise statement, which takes an argument that must be an instance of the class BaseException or a class derived from it.
Step76:  A typical use of exceptions is to abort functions when some error condition occurs, for example
Step77:  To get information about the error, we can access the Exception class instance that describes the exception by using for example
Step78:  Excercise
<ASSISTANT_TASK:>
Python Code:
!ls ../scripts/hello-world*.py
!cat ../scripts/hello-world.py
!python scripts/hello-world.py
Explanation: Introduction to Python
Forked from Lecture 1 of Scientific Python Lectures by J.R. Johansson
Python Program Files
Python code is usually stored in text files with the file ending in ".py":
  myprogram.py
Every line in a Python program file is assumed to be a Python statement, or part thereof. 
The only exception is comment lines, which start with the character # (optionally preceded by an arbitrary number of white-space characters, i.e., tabs or spaces). Comment lines are usually ignored by the Python interpreter.
```
this is a comment
```
To run our Python program from the command line we use:
  $ python myprogram.py
On UNIX systems it is common to define the path to the interpreter on the first line of the program (note that this is a comment line as far as the Python interpreter is concerned):
  #!/usr/bin/env python
If we do, and if we additionally set the file script to be executable, we can run the program like this:
  $ myprogram.py
Example:
End of explanation
import math
Explanation: Jupyter Notebooks
This file - a Jupyter (IPython) notebook - does not follow the standard pattern with Python code in a text file. Instead, an IPython notebook is stored as a file in the JSON format. The advantage is that we can mix formatted text, Python code and code output. It requires the IPython notebook server to run it though, and therefore isn't a stand-alone Python program as described above. Other than that, there is no difference between the Python code that goes into a program file or an IPython notebook.
Modules
Most of the functionality in Python is provided by modules. The Python Standard Library is a large collection of modules that provides cross-platform implementations of common facilities such as access to the operating system, file I/O, string management, network communication, and much more.
References
The Python Language Reference: https://docs.python.org/3/reference/index.html
The Python Standard Library: https://docs.python.org/3/library/
To use a module in a Python program it first has to be imported. A module can be imported using the import statement. For example, to import the module math, which contains many standard mathematical functions, we can do:
End of explanation
import math
x = math.cos(2 * math.pi)
print(x)
Explanation: This includes the whole module and makes it available for use later in the program. For example, we can do:
End of explanation
from math import *
x = cos(2 * pi)
print(x)
Explanation: Alternatively, we can chose to import all symbols (functions and variables) in a module to the current namespace (so that we don't need to use the prefix "math." every time we use something from the math module:
End of explanation
from math import cos, pi
x = cos(2 * pi)
print(x)
Explanation: This pattern can be very convenient, but in large programs that include many modules it is often a good idea to keep the symbols from each module in their own namespaces, by using the import math pattern. This would elminate potentially confusing problems with name space collisions.
As a third alternative, we can chose to import only a few selected symbols from a module by explicitly listing which ones we want to import instead of using the wildcard character *:
End of explanation
import math
print(dir(math))
Explanation: Looking at what a module contains, and its documentation
Once a module is imported, we can list the symbols it provides using the dir function:
End of explanation
help(math.log)
math.log(10)
math.log(10, 2)
Explanation: And using the function help we can get a description of each function (almost .. not all functions have docstrings, as they are technically called, but the vast majority of functions are documented this way).
End of explanation
import os
os.listdir()
Explanation: We can also use the help function directly on modules: Try
help(math)
Some very useful modules form the Python standard library are os, sys, math, shutil, re, subprocess, multiprocessing, threading. 
A complete lists of standard modules for Python 3 are available at http://docs.python.org/3/library/.
For example, this is the os module in the standard library.
End of explanation
# variable assignments
x = 1.0
my_variable = 12.2
Explanation: Variables and types
Symbol names
Variable names in Python can contain alphanumerical characters a-z, A-Z, 0-9 and some special characters such as _. Normal variable names must start with a letter. 
By convention, variable names start with a lower-case letter, and Class names start with a capital letter. 
In addition, there are a number of Python keywords that cannot be used as variable names. These keywords are:
and, as, assert, break, class, continue, def, del, elif, else, except, 
exec, finally, for, from, global, if, import, in, is, lambda, not, or,
pass, print, raise, return, try, while, with, yield
Assignment
The assignment operator in Python is =. Python is a dynamically typed language, so we do not need to specify the type of a variable when we create one.
Assigning a value to a new variable creates the variable:
End of explanation
type(x)
Explanation: Although not explicitly specified, a variable does have a type associated with it. The type is derived from the value that was assigned to it.
End of explanation
x = 1
type(x)
Explanation: If we assign a new value to a variable, its type can change.
End of explanation
import traceback 
try:
    print(y)
except NameError as e:
    print(traceback.format_exc())
Explanation: If we try to use a variable that has not yet been defined we get an NameError:
End of explanation
# integers
x = 1
type(x)
# float
x = 1.0
type(x)
# boolean
b1 = True
b2 = False
type(b1)
# complex numbers: note the use of `j` to specify the imaginary part
x = 1.0 - 1.0j
type(x)
print(x)
print(x.real, x.imag)
Explanation: Fundamental types
End of explanation
x = 1.0
# check if the variable x is a float
type(x) is float
# check if the variable x is an int
type(x) is int
Explanation: Type utility functions
End of explanation
isinstance(x, float)
Explanation: We can also use the isinstance method for testing types of variables:
End of explanation
x = 1.5
print(x, type(x))
x = int(x)
print(x, type(x))
z = complex(x)
print(z, type(z))
import traceback 
try:
    x = float(z)
except TypeError as e:
    print(traceback.format_exc())
Explanation: Type casting
End of explanation
1 + 2, 1 - 2, 1 * 2, 1 / 2
1.0 + 2.0, 1.0 - 2.0, 1.0 * 2.0, 1.0 / 2.0
# Integer division of float numbers
3.0 // 2.0
# Note! The power operators in python isn't ^, but **
2 ** 2
Explanation: Operators and comparisons
Most operators and comparisons in Python work as one would expect:
Arithmetic operators +, -, *, /, // (integer division), '**' power
End of explanation
True and False
not False
True or False
Explanation: Note: The / operator always performs a floating point division in Python 3.x.
This is not true in Python 2.x, where the result of / is always an integer if the operands are integers.
to be more specific, 1/2 = 0.5 (float) in Python 3.x, and 1/2 = 0 (int) in Python 2.x (but 1.0/2 = 0.5 in Python 2.x).
The boolean operators are spelled out as the words and, not, or.
End of explanation
2 > 1, 2 < 1
2 > 2, 2 < 2
2 >= 2, 2 <= 2
# equality
[1,2] == [1,2]
# objects identical?
list1 = list2 = [1,2]
list1 is list2
Explanation: Comparison operators &gt;, &lt;, &gt;= (greater or equal), &lt;= (less or equal), == equality, is identical.
End of explanation
s = "Hello world"
type(s)
# length of the string: the number of characters
len(s)
# replace a substring in a string with somethign else
s2 = s.replace("world", "test")
print(s2)
Explanation: Exercise:
Mindy has $5.25 in her pocket. Apples cost 29 cents each. Calculate how many apples mindy can buy and how much change she will have left. Money should be represented in variables of type float and apples should be represented in variables of type integer.
Answer:
mindy_money = 5.25
apple_cost = .29
num_apples = ?
change = ?
Compound types: Strings, List and dictionaries
Strings
Strings are the variable type that is used for storing text messages.
End of explanation
s[0]
Explanation: We can index a character in a string using []:
End of explanation
s[0:5]
s[4:5]
Explanation: Heads up MATLAB and R users: Indexing start at 0!
We can extract a part of a string using the syntax [start:stop], which extracts characters between index start and stop -1 (the character at index stop is not included):
End of explanation
s[:5]
s[6:]
s[:]
Explanation: If we omit either (or both) of start or stop from [start:stop], the default is the beginning and the end of the string, respectively:
End of explanation
s[::1]
s[::2]
Explanation: We can also define the step size using the syntax [start:end:step] (the default value for step is 1, as we saw above):
End of explanation
print("str1", "str2", "str3")  # The print statement concatenates strings with a space
print("str1", 1.0, False, -1j)  # The print statements converts all arguments to strings
print("str1" + "str2" + "str3") # strings added with + are concatenated without space
print("value = %f" % 1.0)       # we can use C-style string formatting
# this formatting creates a string
s2 = "value1 = %.2f. value2 = %d" % (3.1415, 1.5)
print(s2)
# alternative, more intuitive way of formatting a string 
s3 = 'value1 = {0}, value2 = {1}'.format(3.1415, 1.5)
print(s3)
Explanation: This technique is called slicing. Read more about the syntax here: https://docs.python.org/3.5/library/functions.html#slice
Python has a very rich set of functions for text processing. See for example https://docs.python.org/3.5/library/string.html for more information.
String formatting examples
End of explanation
l = [1,2,3,4]
print(type(l))
print(l)
Explanation: Exercise:
Paste in the code from your previous exercise and output the result as a story (round monetary values to 2 decimal places). The ouptut should look like this:
"Mindy had \$5.25 in her pocket. Apples at her nearby store cost 29 cents. With her \$5.25, mindy can buy 18 apples and will have 10 cents left over."
List
Lists are very similar to strings, except that each element can be of any type.
The syntax for creating lists in Python is [...]:
End of explanation
print(l)
print(l[1:3])
print(l[::2])
Explanation: We can use the same slicing techniques to manipulate lists as we could use on strings:
End of explanation
l[0]
Explanation: Heads up MATLAB and R users: Indexing starts at 0!
End of explanation
l = [1, 'a', 1.0, 1-1j]
print(l)
Explanation: Elements in a list do not all have to be of the same type:
End of explanation
nested_list = [1, [2, [3, [4, [5]]]]]
nested_list
Explanation: Python lists can be heterogeneous and arbitrarily nested:
End of explanation
start = 10
stop = 30
step = 2
range(start, stop, step)
# in python 3 range generates an iterator, which can be converted to a list using 'list(...)'.
# It has no effect in python 2
list(range(start, stop, step))
list(range(-10, 10))
s
# convert a string to a list by type casting:
s2 = list(s)
s2
# sorting lists (by creating a new variable)
s3 = sorted(s2)
print(s2)
print(s3)
# sorting lists in place
s2.sort()
print(s2)
Explanation: Lists play a very important role in Python. For example they are used in loops and other flow control structures (discussed below). There are a number of convenient functions for generating lists of various types, for example the range function:
End of explanation
# create a new empty list
l = []
# add an elements using `append`
l.append("A")
l.append("d")
l.append("d")
print(l)
Explanation: Adding, inserting, modifying, and removing elements from lists
End of explanation
l[1] = "p"
l[2] = "p"
print(l)
l[1:3] = ["d", "d"]
print(l)
Explanation: We can modify lists by assigning new values to elements in the list. In technical jargon, lists are mutable.
End of explanation
l.insert(0, "i")
l.insert(1, "n")
l.insert(2, "s")
l.insert(3, "e")
l.insert(4, "r")
l.insert(5, "t")
print(l)
Explanation: Insert an element at an specific index using insert
End of explanation
l.remove("A")
print(l)
Explanation: Remove first element with specific value using 'remove'
End of explanation
del l[7]
del l[6]
print(l)
Explanation: Remove an element at a specific location using del:
End of explanation
point = (10, 20)
print(point, type(point))
point = 10, 20
print(point, type(point))
Explanation: See help(list) for more details, or read the online documentation 
Tuples
Tuples are like lists, except that they cannot be modified once created, that is they are immutable. 
In Python, tuples are created using the syntax (..., ..., ...), or even ..., ...:
End of explanation
x, y = point
print("x =", x)
print("y =", y)
Explanation: We can unpack a tuple by assigning it to a comma-separated list of variables:
End of explanation
try:
    point[0] = 20
except TypeError as e:
    print(traceback.format_exc())
Explanation: If we try to assign a new value to an element in a tuple we get an error:
End of explanation
params = {"parameter1" : 1.0,
          "parameter2" : 2.0,
          "parameter3" : 3.0,}
print(type(params))
print(params)
print("parameter1 = " + str(params["parameter1"]))
print("parameter2 = " + str(params["parameter2"]))
print("parameter3 = " + str(params["parameter3"]))
params["parameter1"] = "A"
params["parameter2"] = "B"
# add a new entry
params["parameter4"] = "D"
print("parameter1 = " + str(params["parameter1"]))
print("parameter2 = " + str(params["parameter2"]))
print("parameter3 = " + str(params["parameter3"]))
print("parameter4 = " + str(params["parameter4"]))
Explanation: Dictionaries
Dictionaries are also like lists, except that each element is a key-value pair. The syntax for dictionaries is {key1 : value1, ...}:
End of explanation
statement1 = False
statement2 = False
if statement1:
    print("statement1 is True")
    
elif statement2:
    print("statement2 is True")
    
else:
    print("statement1 and statement2 are False")
Explanation: Exercise:
Mindy doesn't want 18 apples, that's too many for someone who lives by themselves. We're now going to represent mindy's world using our new data types.
Make a list containing the fruits that mindy desires. She likes apples, strawberries, pinapples, and papayas.
Make a tuple containing the fruits that the store has. This is immutable because the store doesn't change their inventory. The local store has apples, strawberries, pinapples, pears, bananas, and oranges.
Make a dictonary showing the price of each fruit at the store. Apples are 29 cents, bananas are 5 cents, oranges are 20 cents, strawberries are 30 cents and pinapples are $1.50.
Control Flow
Conditional statements: if, elif, else
The Python syntax for conditional execution of code uses the keywords if, elif (else if), else:
End of explanation
statement1 = statement2 = True
if statement1:
    if statement2:
        print("both statement1 and statement2 are True")
# # Bad indentation!
# if statement1:
#     if statement2:
#     print("both statement1 and statement2 are True")  # this line is not properly indented
statement1 = False 
if statement1:
    print("printed if statement1 is True")
    
    print("still inside the if block")
if statement1:
    print("printed if statement1 is True")
    
print("now outside the if block")
Explanation: For the first time, here we encounted a peculiar and unusual aspect of the Python programming language: Program blocks are defined by their indentation level. 
Compare to the equivalent C code:
if (statement1)
{
    printf("statement1 is True\n");
}
else if (statement2)
{
    printf("statement2 is True\n");
}
else
{
    printf("statement1 and statement2 are False\n");
}
In C blocks are defined by the enclosing curly brakets { and }. And the level of indentation (white space before the code statements) does not matter (completely optional). 
But in Python, the extent of a code block is defined by the indentation level (usually a tab or say four white spaces). This means that we have to be careful to indent our code correctly, or else we will get syntax errors. 
Examples:
End of explanation
for x in [1,2,3]:
    print(x)
Explanation: Loops
In Python, loops can be programmed in a number of different ways. The most common is the for loop, which is used together with iterable objects, such as lists. The basic syntax is:
for loops:
End of explanation
for x in range(4): # by default range start at 0
    print(x)
Explanation: The for loop iterates over the elements of the supplied list, and executes the containing block once for each element. Any kind of list can be used in the for loop. For example:
End of explanation
for x in range(-3,3):
    print(x)
for word in ["scientific", "computing", "with", "python"]:
    print(word)
Explanation: Note: range(4) does not include 4 !
End of explanation
for key, value in params.items():
    print(key + " = " + str(value))
Explanation: To iterate over key-value pairs of a dictionary:
End of explanation
for idx, x in enumerate(range(-3,3)):
    print(idx, x)
Explanation: Sometimes it is useful to have access to the indices of the values when iterating over a list. We can use the enumerate function for this:
End of explanation
l1 = [x**2 for x in range(0,5)]
print(l1)
Explanation: List comprehensions: Creating lists using for loops:
A convenient and compact way to initialize lists:
End of explanation
i = 0
while i < 5:
    print(i)
    
    i = i + 1
    
print("done")
Explanation: while loops:
End of explanation
def func0():   
    print("test")
func0()
Explanation: Note that the print("done") statement is not part of the while loop body because of the difference in indentation.
Exercise:
Loop through all of the fruits that mindy wants and check if the store has them. For each fruit that she wants print 
"Mindy, the store has apples and they cost $.29"
or
"Mindy, the store does not have papayas"
Functions
A function in Python is defined using the keyword def, followed by a function name, a signature within parentheses (), and a colon :. The following code, with one additional level of indentation, is the function body.
End of explanation
def func1(s):
    
    Print a string 's' and tell how many characters it has    
    
    
    print(s + " has " + str(len(s)) + " characters")
help(func1)
func1("test")
Explanation: Optionally, but highly recommended, we can define a so called "docstring", which is a description of the functions purpose and behaivor. The docstring should follow directly after the function definition, before the code in the function body.
End of explanation
def square(x):
    
    Return the square of x.
    
    return x ** 2
square(4)
Explanation: Functions that returns a value use the return keyword:
End of explanation
def powers(x):
    
    Return a few powers of x.
    
    return x ** 2, x ** 3, x ** 4
powers(3)
x2, x3, x4 = powers(3)
print(x3)
Explanation: We can return multiple values from a function using tuples (see above):
End of explanation
def myfunc(x, p=2, debug=False):
    if debug:
        print("evaluating myfunc for x = " + str(x) + " using exponent p = " + str(p))
    return x**p
Explanation: Default argument and keyword arguments
In a definition of a function, we can give default values to the arguments the function takes:
End of explanation
myfunc(5)
myfunc(5, debug=True)
Explanation: If we don't provide a value of the debug argument when calling the the function myfunc it defaults to the value provided in the function definition:
End of explanation
myfunc(p=3, debug=True, x=7)
Explanation: If we explicitly list the name of the arguments in the function calls, they do not need to come in the same order as in the function definition. This is called keyword arguments, and is often very useful in functions that takes a lot of optional arguments.
End of explanation
f1 = lambda x: x**2
    
# is equivalent to 
def f2(x):
    return x**2
f1(2), f2(2)
Explanation: Unnamed functions (lambda function)
In Python we can also create unnamed functions, using the lambda keyword:
End of explanation
# map is a built-in python function
map(lambda x: x**2, range(-3,4))
# in python 3 we can use `list(...)` to convert the iterator to an explicit list
list(map(lambda x: x**2, range(-3,4)))
Explanation: This technique is useful for example when we want to pass a simple function as an argument to another function, like this:
End of explanation
class Point:
    
    Simple class for representing a point in a Cartesian coordinate system.
    
    
    def __init__(self, x, y):
        
        Create a new Point at x, y.
        
        self.x = x
        self.y = y
        
    def translate(self, dx, dy):
        
        Translate the point by dx and dy in the x and y direction.
        
        self.x += dx
        self.y += dy
        
    def __str__(self):
        return("Point at [%f, %f]" % (self.x, self.y))
Explanation: Exercise:
Mindy is great, but we want code that can tell anyone what fruits the store has. To do this we will generalize our code for Mindy using a function.
Write a function that takes the following parameters
- full_name (string)
- fruits_you_want (list)
- fruits_the_store_has (tuple)
- prices (dict)
and prints to the terminal a sentence per fruit that you want just like the last exercise. For example, if 
name = 'Al'
list_of_fruits_you_want = ['apple', 'banana']
tuple_of_fruits_the_store_has = ('apple', 'banana', 'orange', 'strawberries', 'pineapple')
prices = {
    'apple' : .29
    'banana':  .05
    'orange': .20
    'strawberries': .30
    'pinapple': 1.50
}
The function should print.
"Al, the store has apples and they cost \$.29"
"Al, the store has bananas and they cost \$.05"
Classes
Classes are the key features of object-oriented programming. A class is a structure for representing an object and the operations that can be performed on the object. 
In Python a class can contain attributes (variables) and methods (functions).
A class is defined almost like a function, but using the class keyword, and the class definition usually contains a number of class method definitions (a function in a class).
Each class method should have an argument self as its first argument. This object is a self-reference.
Some class method names have special meaning, for example:
__init__: The name of the method that is invoked when the object is first created.
__str__ : A method that is invoked when a simple string representation of the class is needed, as for example when printed.
There are many more, see http://docs.python.org/2/reference/datamodel.html#special-method-names
End of explanation
p1 = Point(0, 0) # this will invoke the __init__ method in the Point class
print(p1)         # this will invoke the __str__ method
Explanation: To create a new instance of a class:
End of explanation
p2 = Point(1, 1)
p1.translate(0.25, 1.5)
print(p1)
print(p2)
Explanation: To invoke a class method in the class instance p:
End of explanation
%%file mymodule.py
Example of a python module. Contains a variable called my_variable,
a function called my_function, and a class called MyClass.
my_variable = 0
def my_function():
    
    Example function
    
    return my_variable
    
class MyClass:
    
    Example class.
    
    def __init__(self):
        self.variable = my_variable
        
    def set_variable(self, new_value):
        
        Set self.variable to a new value
        
        self.variable = new_value
        
    def get_variable(self):
        return self.variable
Explanation: Note that calling class methods can modifiy the state of that particular class instance, but does not effect other class instances or any global variables.
That is one of the nice things about object-oriented design: code such as functions and related variables are grouped in separate and independent entities. 
Modules
One of the most important concepts in good programming is to reuse code and avoid repetitions.
The idea is to write functions and classes with a well-defined purpose and scope, and reuse these instead of repeating similar code in different part of a program (modular programming). The result is usually that readability and maintainability of a program is greatly improved. What this means in practice is that our programs have fewer bugs, are easier to extend and debug/troubleshoot. 
Python supports modular programming at different levels. Functions and classes are examples of tools for low-level modular programming. Python modules are a higher-level modular programming construct, where we can collect related variables, functions and classes in a module. A python module is defined in a python file (with file-ending .py), and it can be made accessible to other Python modules and programs using the import statement. 
Consider the following example: the file mymodule.py contains simple example implementations of a variable, function and a class:
End of explanation
import mymodule
Explanation: We can import the module mymodule into our Python program using import:
End of explanation
help(mymodule)
mymodule.my_variable
mymodule.my_function() 
my_class = mymodule.MyClass() 
my_class.set_variable(10)
my_class.get_variable()
Explanation: Use help(module) to get a summary of what the module provides:
End of explanation
import importlib
importlib.reload(mymodule) # Python 3 only
# For Python 2 use reload(mymodule)
Explanation: If we make changes to the code in mymodule.py, we need to reload it using reload:
End of explanation
try:
    raise Exception("description of the error")
except Exception as e:
    print(traceback.format_exc())
Explanation: Exceptions
In Python errors are managed with a special language construct called "Exceptions". When errors occur exceptions can be raised, which interrupts the normal program flow and fallback to somewhere else in the code where the closest try-except statement is defined.
To generate an exception we can use the raise statement, which takes an argument that must be an instance of the class BaseException or a class derived from it.
End of explanation
try:
    print("test")
    # generate an error: the variable test is not defined
    print(test)
except Exception:
    print("Caught an exception")
Explanation: A typical use of exceptions is to abort functions when some error condition occurs, for example:
def my_function(arguments):
    if not verify(arguments):
        raise Exception("Invalid arguments")
    # rest of the code goes here
To gracefully catch errors that are generated by functions and class methods, or by the Python interpreter itself, use the try and  except statements:
try:
    # normal code goes here
except:
    # code for error handling goes here
    # this code is not executed unless the code
    # above generated an error
For example:
End of explanation
try:
    print("test")
    # generate an error: the variable test is not defined
    print(test)
except Exception as e:
    print("Caught an exception:", e)
Explanation: To get information about the error, we can access the Exception class instance that describes the exception by using for example:
except Exception as e:
End of explanation
%reload_ext version_information
%version_information
Explanation: Excercise:
Make two classes with the following variables and methods
Store
variables
- inventory (dict)
methods
- show_inventory()
 * nicely displays the store's inventory
- message_customer(customer)
 * shows the customer if the store has the fruits they want and how much each fruit costs (this is code from previous exercises, except it will use the customer's "Formal Greeting" instead of their first name.)  
Customer 
variables
- first_name (string)
- last_name (string)
- is_male (boolean)
- money (float)
- fruit (dict)
- preferred_fruit (list)
methods
- formal_greeting()
    * (Mr. Al Johri, Ms. Mindy Smith)
- buy_fruit(store, fruit_name, fruit_amt)
    * inputs are a store, the name of a fruit, and the amount of that fruit
    * checks to see if the store has the fruit - returns an error if it does not
    * checks to see if the customer can afford the amount of fruit they intend to buy - returns an error if not
    * "purchases" the fruit by adding it to the customers fruit dict and removes the correct amount of money from their money variable
Exercise:
Instantiate a list of the following customers.
Mindy Smith - \$5.25, likes apples and oranges
Al Johri - \$20.19, likes papaya, strawberries, pinapple, and apples  
Hillary Clinton - \$15, likes strawberries and oranges  
Oliver Twist - \$.05, likes apples  
Donald Trump - \$4000, only likes durian
Create a store called Whole Foods with the following inventory
   * 'apple' : \$.29
   * 'banana':  \$.05
   * 'orange': \$.20
   * 'strawberries': \$.30
   * 'pinapple': \$1.50,
   * 'grapes': \$.22,
   * 'durian': \$5000
Write code to do the following
Print the store's inventory
For each customer, print the store's message to them
Have each customer purchase 1 of each fruit in their list of preferred fruits. 
(Make sure you have error handling so the program doesn't halt if the store doesn't have the fruit the customer wants or if the customer doesn not have enough money to buy the fruit.)
Bonus Exercise
Organize the code! Make a module called fruits (in a file fruits.py) that contains the class definitions.
Make a separate cell (in a file main.py) which imports the fruits module, instantiates the store and the list of customers, and runs the code in the previous exercise.
Further reading
http://www.python.org - The official web page of the Python programming language.
https://docs.python.org/3/tutorial/ - The Official Python Tutorial
http://www.python.org/dev/peps/pep-0008 - Style guide for Python programming. Highly recommended. 
http://www.scipy-lectures.org/intro/language/python_language.html - Scipy Lectures: Lecture 1.2
Versions
End of explanation
<END_TASK>