Class 13

- Local Variables
- Passing Values to Functions
- Passing Multiple Arguments
- Default Values for Parameters
- Global Variables
- Global Constants

- Functions that Return Values
- Returning Multiple Values
- Standard Library Functions
- Creating Random Numbers
- The random Module
- Other random Functions
- Random Number Seeds

You should read Chapter 7, *Files and Exceptions*, from our
textbook, Starting Out with Python,
before the week **after** the Mid-term

I have posted homework 7 here.

It is due Sunday, October 29th, at 11:59 PM.

It is **not** due this coming Sunday to allow you to study for the Mid-term Exam.

The mid-term exam will be given on Tuesday, October 24th.

It will consist of questions like those on the quizzes along with questions asking you to write short segments of Python code.

60% of the points on this exam will consist of questions from the Ungraded Class Quizzes.

The next class, Thursday, October 19th, will be a review session.

You will only be responsible for the material in the Class Notes for that class on the exam.

The Mid-term is a closed book exam.

- Any time you create a variable inside a function ...
- you are creating a local variable
- A local variable created inside a function ..
- can only be used inside that function
- The variable only exists while the function runs
- This means the variable is invisible outside the function
- Outside the function, the
**variable does not exist** - Local variables defined in one function ...
- are invisible to other functions
- This means we can use the same variable name ...
- in more than one function
- Another way of saying this is that the scope of a local variable ...
- is the function in which it is defined

- We give values to a function using parameters
- Parameters are the are the variables you define in the function header ...
- inside parentheses
- Parameters are local variables ...
- so they have no meaning outside of the function
- But they are special local variables ...
- because they get their values from the function call
- Let's say I have the following function
def cheer(team): print('Go', team)

- team is the parameter
- I use the function with a function call
- which consists of the name of the function ...
- and some expressions inside the parentheses ...
- after the function name
- Normally there is one expression ...
- for each parameter in the function definition
- The expressions inside the parentheses of a function call are called arguments
- When I call the function cheer like this
cheer('Red Sox')

- 'Red Sox' is the argument
- When the function comes to life ...
- the value of the parameter team ...
- is set to 'Red Sox'
- and the function prints
Go Red Sox

- So parameters are special local variables ...
- whose names appear inside the parentheses of the function header ...
- and they get their value from the arguments ...
- in the function call

- Function can have more than one parameter
- When they do you must give them as many arguments ...
- as there are parameters in the function definition ...
- unless some parameters have default values
- The argument values are matched with the parameters ...
- by position
- So the first argument in the function call ...
- gives a value to the first parameter ...
- the second argument give value to the second parameter ...
- and so on
- If you don't supply a one of the parameters ...
- that does not have a default value ...
- you will get an error

- The problem with having a function with many parameters ...
- is that you have to supply a value for each parameter ...
- or you will get an error
- This is particularly annoying ...
- when one of the parameter usually has the same value
- It's annoying to have to supply additional arguments
- when their value almost never changes
- Fortunately, Python let's you supply default values in the function call ...
- which will be automatically assigned to its corresponding parameter ...
- if it is omitted in the function call
- The format for writing a function with default values is
`def FUNCTION_NAME([PARAMETER, ...], PARAMETER_NAME=DEFAULT_VALUE [, PARAMETER_NAME=DEFAULT_VALUE]): STATEMENT ...`

- Notice that the default values must come at the end of the parameter list ...
- and that you can have more than one

- When a variable is created outside of
**all**the functions in a script ... - it can be used inside
**any**function - Variables created outside of all functions are called global variables
- In general, you should avoid using global variables ...
- because they can make a program harder to understand
- They can also cause bugs that are hard to find
- A global variable can be changed by any statement
- If you have a bug involving a global variable ...
- you will have to find every statement in the program that changes the variable
- Writing functions that use global variables make it hard to write functions ...
- that can be used in more than one program
- Don't use global variables unless you have a
**very**good reason

- Global variables in Python have one useful feature ...
- that can be helpful in writing programs
- The value of a a global variable cannot be changed inside a function ...
- unless you use a special feature of Python
- This makes them useful for storing values that do not change
- Variables that cannot changed are called constants
- Here are some examples of constants
- The number of days in the week
- The number of months in a year
- The first date of the year
- The ration of the circumference of a circle to its diameter (π)

- Python does not have true constants ...
- but you can use global variables to hold constant value
- It is customary to write the names of constants in ALL_CAPITALS
- Global variables used this way should be declared once ...
- at the top of the program ...
- and never changed

- Functions that return values can be used in assignment statements
`>>> result = math.sqrt(7) >>> result 2.6457513110645907`

- For a function to give a value back ...
- to the Python statement that called it ...
- it must use the
`return`

keyword - A return statement
is a Python statement that uses the
`return`

keyword ... - and has the format
`return EXPRESSION`

- Remember, an expression is anything that the Python interpreter can turn into a value
- A
`return`

statement does two things- It ends the execution of the function
- It sends a value back to the function call

- Since a
`return`

statement always causes the function to quit ... - any Python statements after
`return`

will never be executed - Here is a new version of the cheer function ...
- that returns a value
>>> def cheer(team): ... return 'Go ' + team + '!'

- Since this version does not actually print anything ...
- we need to put the call to the function ...
- inside a call to
`print`

>>> print(cheer('Red Sox')) Go Red Sox!

- We can do this because this version of cheer returns a value ...
- so it is an expression ...
- and we can always use an expression as the argument to a function

- A Python
`return`

statement can return more than one value - All you have to do is follow the
`return`

keyword ... - with a comma separated list of expressions
`return EXPRESION, EXPRESSION [, EXPRESSION, ...]`

- Using this feature I can write a function to get both the first and last name
>>> def get_full_name(): ... first_name = input("Please enter your first name: ") ... last_name = input("Please enter your last name: ") ... return first_name, last_name ...

- When this function is called it will ask for two values ...
- and return each to a separate variable
>>> fname, lname = get_full_name() Please enter your first name: Glenn Please enter your last name: Hoffman

- which I can use
>>> print("Hello", fname, lname) Hello Glenn Hoffman

- All computer languages come with functions that perform useful operations
- Here are some of the Python functions we have used
- int
- float
- input
- range

- All of these are built-in functions
- Built-in functions are actually part of the Python interpreter ...
- so they are always available
- You do not have to do anything special to use them
- But Python also have other useful functions ...
- found in the standard_library
- Each standard library is a collection of functions and constants ...
- that deal with a specific topic ...
- and are copied to any machine which installs Python
- Each one of these libraries is contained in a module
- A module is a file containing Python statements ...
- most often functions and variables
- You can use any of the functions and variables stored in a module ...
- by importing them using the
`import`

keyword - For example, the math library contains many mathematical functions ...
- such as
- log - Logarithm to base e
- log10 - Logarithm to base 10
- sin - Sine
- cos - Cosine
- tan - Tangent
- ceil - Ceiling, the smallest integer larger than some value
- floor - Floor, the largest integer smaller than some value

- It also comes with some constants
- pi - the ratio of the circumference of a circle to its diameter
- e - the base of the natural logarithms

- If we want to use any part of the math library ...
- we must first import it
>>> import math

- Using something in a standard library is different from using a built-in
- When we use a built-in we simply type the name
>>> print("Hello") Hello

- But when you need to use something contained in a module ...
- you must first type the name of the module ...
- followed by a dot, . ...
- followed by the name of the function or variable
- This is called dot notation
- Here are some examples
>>> import math >>> math.pi 3.141592653589793 >>> math.sin(math.pi) 1.2246467991473532e-16 >>> math.cos(math.pi) -1.0 >>> math.ceil(4.3) 5 >>> math.floor(4.3) 4 >>> math.floor(-4.3) -5 >>> math.ceil(-4.3) -4

- Many things in nature are random
- The number of dust particles in a room
- Whether a coin comes up heads or tails
- The number of leaves in my yard
- The number of water droplets in a cloud

- Random events are events that cannot be predicted
- In other word, events with no patterns
- When writing programs, we often want to use random numbers
- We need them in writing
- Games
- Simulations

- How much fun would a game be ...
- if every game produced the same monsters ...
- coming from the same direction ...
- at the same period of time?
- Simulations are extremely important in solving problems in physics ...
- Like the evolution of a galaxy
- We would like to have functions that can generate random numbers ...
- for use in such situations
- But we have a problem
- When we write a function ...
- we determine exactly what the output will be
- Every time we run the program ...
- the same input should give the same output
- In other words, the output of a program is completely determined
- There is nothing random about it
- But the need for random numbers in computing is so great ...
- that many smart people have developed algorithms ...
- that produce pseudorandom numbers
- Pseudorandom numbers are number that occurs in a sequence ...
- that eventually repeats ...
- but the interval between repetitions is so long ...
- that they are random enough for writing programs
- The functions that create these pseudorandom sequences ...
- are called pseudorandom number generators ...
- or simply random number generators ...
- although that term in not really accurate

- One of the modules in the standard library is random
- It contains several several functions that can create pseudorandom numbers
- The function randint takes two integers as arguments ...
- and returns a pseudorandom integer
- The two numbers represent the minimum and the maximum ...
- of the range of numbers created
- We can call this function several times
- an each time it will return a different number
$ cat random_1.py # demonstrates the use of the randint function of the random module import random for i in range(10): print(random.randint(1,100)) $ python3 random_1.py 89 98 93 73 32 40 63 100 76 80 $ python3 random_1.py 66 49 1 29 63 17 91 3 70 5

- We can use randint to simulate throwing dice
$ cat dice.py # this program emulates a throw of two dice import random die_1 = random.randint(1,6) die_2 = random.randint(1,6) print("You rolled", str(die_1), "and", str(die_2), "for a total of", str(die_1 + die_2)) $ python3 dice.py You rolled 4 and 3 for a total of 7 $ python3 dice.py You rolled 7 and 1 for a total of 8 $ python3 dice.py You rolled 5 and 3 for a total of 8

- If we called randint long enough ...
- we would eventually repeat the sequence of values
- But we would have to call it a very large number of times

- The random module has other functions to generate pseudorandom numbers
- Each of them has advantages in different situations
- The randrange function also returns an integer ...
- but it gives you more options when specifying the range
- randint takes two arguments ...
- which specify the range of values you want
- The arguments to the randrange function ...
- work just like the arguments to the
`range`

function - When run with one argument ...
- randrange creates number in the range from 0 ...
- to
**one less**than the argument$ cat random_2.py # demonstrates the use of the randrange function of the random module # with one argument import random for i in range(10): print(random.randrange(5)) $ python3 random_2.py 1 3 2 0 1 1 2 2 0 4

- Notice that all the numbers are between 0 and 4
- When called with two arguments ...
- the first argument specifies the start of the range ...
- and the second number specifies one more than the end of the range
$ cat random_3.py # demonstrates the use of the randrange function of the random module # with two arguments import random for i in range(10): print(random.randrange(1, 6)) $ python3 random_3.py 1 3 1 2 5 5 2 3 1 1

- Here all the numbers fall between 1 and 5
- When called with 3 arguments ...
- have the same function as in the two argument case ...
- and the third argument specifies the difference between any two values
$ cat random_4.py # demonstrates the use of the randrange function of the random module # with three arguments import random for i in range(10): print(random.randrange(2, 11, 2)) $ python3 random_4.py 6 6 4 6 8 10 10 4 8 2

- The values are all even numbers between 2 and 10
- Both randint and randrange return integers
- If you want a decimal number, use random
- It generates a random number between 0.0 and 1.0
$ cat random_5.py # demonstrates the use of the random function of the random module import random for i in range(10): print(random.random()) [715] glenn - ~/workspace-kepler/it116/resources_it116/code_it116/examples/5_chapter_examples $ python3 random_5.py 0.3366683809865726 0.6243291094221154 0.47182435685723234 0.3079697111617222 0.5048399470937616 0.11682962682702247 0.08597187089498437 0.10398098486344709 0.8667164814826076 0.15337564523669833

- If you want a random decimal, but want to specify the range of values ...
- use uniform
$ cat random_6.py # demonstrates the use of the uniform function of the random module import random for i in range(10): print(random.uniform(1, 5)) $ python3 random_6.py 3.987702460027857 2.776217942732739 2.890534381287354 1.5377836190792888 3.1461905324732022 1.010886780905638 1.4452549292653458 4.558908792372804 4.934363837349949 2.1135109420482228

- The numbers generated by the functions in the random module ...
- are not truly random
- They are created by specific algorithms ...
- which generate a series of numbers ...
- that only repeat after a
**very**long interval - The formula that generates these values requires a starting value ...
- called a seed
- If the same value is chosen for the seed each time a program runs ...
- the sequence of numbers created by the random functions ...
- will be the same
- To prevent this from happening the function in random use a trick
- Instead of choosing the same value for the seed ...
- each time they run ...
- they use the system time instead
- Every computer keeps track of the time and date ...
- in a variable called the system time
- The system time is a very large integer ...
- and it keeps changing
- Choosing the seed in this manner means the functions in random ...
- are very unlikely two create the same sequence of numbers ...
- each time a program runs
- But what if we wanted the sequence of numbers to be the same?
- Let's say that you were writing a simulation of a complicated system
- Say the weather over a continent
- You run the simulation once and get a result
- Then you want to know what would change ...
- if you added another variable to the simulation
- Say you added calculations to your weather simulator ...
- to take account of the presence of dust in the atmosphere
- Dust helps rain clouds form
- It would be good if we could use the same "random" sequence of numbers ...
- in the second run of the simulation ...
- as we did in the first
- We can do this using the seed function
- The seed function assigns a specific value to the seed ...
- used by all random functions
- If we use the seed function to set the seed value ...
- each time the program is run, we will get the same series of numbers
$ cat random_6.py # demonstrates the use of the seed function in the random module # to generate the same series of numbers each time the program is run import random random.seed(67) for i in range(10): print(random.randint(1, 100)) $ python3 random_6.py 10 15 99 53 60 54 35 77 55 63 [725] glenn - ~/workspace-kepler/it116/resources_it116/code_it116/examples/5_chapter_examples $ python3 random_6.py 10 15 99 53 60 54 35 77 55 63