0% found this document useful (0 votes)
4 views

Conditional Functions Iterations

Uploaded by

shashwat
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Conditional Functions Iterations

Uploaded by

shashwat
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 107

Conditional Execution

Function
Loops and Iteration
x=5
Conditional Steps
Yes
x < 10 ?

No print('Smaller') Program:
Output:
x = 5
Yes if x < 10: Smaller
x > 20 ? print('Smaller') Finis
if x > 20:
No print('Bigger') print('Bigger')

print('Finis')

print('Finis')
Comparison Operators
• Boolean expressions ask a Python Meaning
question and produce a Yes or No < Less than
result which we use to control
program flow <= Less than or Equal to
== Equal to
• Boolean expressions using >= Greater than or Equal to
comparison operators evaluate to
> Greater than
True / False or Yes / No
!= Not equal
• Comparison operators look at
variables but do not change the Remember: “=” is used for assignment.
variables
http://en.wikipedia.org/wiki/George_Boole
Comparison Operators
x = 5
if x == 5 :
print('Equals 5') Equals 5
if x > 4 :
print('Greater than 4')
Greater than 4
if x >= 5 : Greater than or Equals 5
print('Greater than or Equals 5')
if x < 6 : print('Less than 6') Less than 6
if x <= 5 :
print('Less than or Equals 5') Less than or Equals 5
if x != 6 :
print('Not equal 6') Not equal 6
One-Way Decisions
x = 5 Yes
print('Before 5') Before 5 x == 5 ?
if x == 5 :
print('Is 5') Is 5 print('Is 5’)
No
print('Is Still 5')
Is Still 5
print('Third 5')
print('Afterwards 5')
Third 5 print('Still 5')
print('Before 6') Afterwards 5
if x == 6 : Before 6 print('Third 5')
print('Is 6')
print('Is Still 6')
print('Third 6')
print('Afterwards 6') Afterwards 6
Indentation
• Increase indent indent after an if statement or for statement (after : )

• Maintain indent to indicate the scope of the block (which lines are affected
by the if/for)

• Reduce indent back to the level of the if statement or for statement to


indicate the end of the block

• Blank lines are ignored - they do not affect indentation

• Comments on a line by themselves are ignored with regard to indentation


Warning: Turn Off Tabs!!
• Atom automatically uses spaces for files with ".py" extension (nice!)

• Most text editors can turn tabs into spaces - make sure to enable this
feature

- NotePad++: Settings -> Preferences -> Language Menu/Tab Settings

- TextWrangler: TextWrangler -> Preferences -> Editor Defaults

• Python cares a *lot* about how far a line is indented. If you mix tabs and
spaces, you may get “indentation errors” even if everything looks fine
This will save you
much unnecessary
pain.
increase / maintain after if or for
decrease to indicate end of block
x = 5
if x > 2 :
print('Bigger than 2')
print('Still bigger')
print('Done with 2')

for i in range(5) :
print(i)
if i > 2 :
print('Bigger than 2')
print('Done with i', i)
print('All Done')
Think About begin/end Blocks
x = 5
if x > 2 :
print('Bigger than 2')
print('Still bigger')
print('Done with 2')

for i in range(5) :
print(i)
if i > 2 :
print('Bigger than 2')
print('Done with i', i)
print('All Done')
Nested x>1
yes

Decisions no print('More than one’)

x = 42
if x > 1 : yes
print('More than one') x < 100
if x < 100 :
no
print('Less than 100') print('Less than 100')
print('All done')

print('All Done')
Two-way Decisions
x=4

• Sometimes we want to
do one thing if a logical no yes
x>2
expression is true and
something else if the
expression is false print('Not bigger') print('Bigger')

• It is like a fork in the


road - we must choose
one or the other path but print('All Done')
not both
Two-way Decisions
x=4
with else:
no yes
x = 4 x>2

if x > 2 :
print('Bigger') print('Not bigger') print('Bigger')
else :
print('Smaller')

print('All done')
print('All Done')
Visualize Blocks x=4

no yes
x = 4 x>2

if x > 2 :
print('Bigger') print('Not bigger') print('Bigger')
else :
print('Smaller')

print('All done')
print('All Done')
More Conditional Structures…
Multi-way
yes
x<2 print('small')
no
if x < 2 :
yes
print('small')
elif x < 10 :
x < 10 print('Medium')
print('Medium') no
else :
print('LARGE') print('LARGE')
print('All done')

print('All Done')
x=0
Multi-way
yes
x<2 print('small')
x = 0
no
if x < 2 :
yes
print('small')
elif x < 10 :
x < 10 print('Medium')
print('Medium') no
else :
print('LARGE') print('LARGE')
print('All done')

print('All Done')
x=5
Multi-way
yes
x<2 print('small')
x = 5
no
if x < 2 :
yes
print('small')
elif x < 10 :
x < 10 print('Medium')
print('Medium') no
else :
print('LARGE') print('LARGE')
print('All done')

print('All Done')
x = 20
Multi-way
yes
x<2 print('small')
x = 20
no
if x < 2 :
yes
print('small')
elif x < 10 :
x < 10 print('Medium')
print('Medium') no
else :
print('LARGE') print('LARGE')
print('All done')

print('All Done')
Multi-way if x < 2 :
print('Small')
elif x < 10 :
# No Else print('Medium')
x = 5 elif x < 20 :
if x < 2 : print('Big')
print('Small') elif x < 40 :
elif x < 10 : print('Large')
print('Medium') elif x < 100:
print('Huge')
print('All done') else :
print('Ginormous')
Multi-way Puzzles
Which will never print
regardless of the value for x?
if x < 2 :
print('Below 2')
if x < 2 : elif x < 20 :
print('Below 2') print('Below 20')
elif x >= 2 : elif x < 10 :
print('Two or more') print('Below 10')
else : else :
print('Something else') print('Something else')
The try / except Structure

• You surround a dangerous section of code with try and except

• If the code in the try works - the except is skipped

• If the code in the try fails - it jumps to the except section


$ python3 notry.py
Traceback (most recent call last):
File "notry.py", line 2, in <module>
istr = int(astr)ValueError: invalid literal
for int() with base 10: 'Hello Bob'
$ cat notry.py
astr = 'Hello Bob' All
istr = int(astr) Done
print('First', istr)
astr = '123'
istr = int(astr)
print('Second', istr)
$ python3 notry.py
Traceback (most recent call last):
File "notry.py", line 2, in <module>
The istr = int(astr)ValueError: invalid literal
program for int() with base 10: 'Hello Bob'
stops $ cat notry.py
here astr = 'Hello Bob' All
istr = int(astr) Done
print('First', istr)
astr = '123'
istr = int(astr)
print('Second', istr)
Generic
Software
Computer
Input
Central
Devices
Processing
Unit
Secondary
Memory

Output Main
Devices Memory
Generic
Software
Computer
Input
Central
Devices
Processing
Unit
Secondary
Memory

Output Main
Devices Memory
astr = 'Hello Bob' When the first conversion fails - it
try: just drops into the except: clause
istr = int(astr) and the program continues.
except:
istr = -1
$ python tryexcept.py
print('First', istr) First -1
Second 123
astr = '123'
try:
istr = int(astr)
except:
istr = -1 When the second conversion
succeeds - it just skips the except:
print('Second', istr) clause and the program continues.
astr = 'Bob'
try / except
print('Hello')
astr = 'Bob'
try:
print('Hello') istr = int(astr)
istr = int(astr)
print('There')
except: print('There')
istr = -1
istr = -1
print('Done', istr)

print('Done', istr) Safety net


Sample try / except
rawstr = input('Enter a number:')
try:
ival = int(rawstr) $ python3 trynum.py
except: Enter a number:42
ival = -1 Nice work
$ python3 trynum.py
if ival > 0 : Enter a number:forty-two
print('Nice work') Not a number
else: $
print('Not a number')
Summary
• Comparison operators • Nested Decisions
== <= >= > < !=
• Multi-way decisions using elif
• Indentation
• try / except to compensate for
• One-way Decisions errors
• Two-way decisions:
if: and else:
Exercise

Rewrite your pay computation to give the


employee 1.5 times the hourly rate for hours
worked above 40 hours.

Enter Hours: 45
Enter Rate: 10

Pay: 475.0
475 = 40 * 10 + 5 * 15
Exercise

Rewrite your pay program using try and except so


that your program handles non-numeric input
gracefully.

Enter Hours: 20
Enter Rate: nine
Error, please enter numeric input

Enter Hours: forty


Error, please enter numeric input
FUNCTION
Stored (and reused) Steps
def
thing(): Program:
print('Hello') Output:
def thing():
print('Fun') print('Hello')
print('Fun') Hello
thing() Fun
thing()
print('Zip') Zip
print('Zip') thing()
Hello
Fun
thing()
We call these reusable pieces of code “functions”
Python Functions
• There are two kinds of functions in Python.

- Built-in functions that are provided as part of Python - print(),


input(), type(), float(), int() ...

- Functions that we define ourselves and then use

• We treat the built-in function names as “new” reserved words


(i.e., we avoid them as variable names)
Function Definition
• In Python a function is some reusable code that takes
arguments(s) as input, does some computation, and then returns
a result or results

• We define a function using the def reserved word

• We call/invoke the function by using the function name,


parentheses, and arguments in an expression
Argument

big = max('Hello world')


Assignment
'w'

Result
>>> big = max('Hello world')
>>> print(big)
w
>>> tiny = min('Hello world')
>>> print(tiny)

>>>
Max Function
A function is some
>>> big = max('Hello world') stored code that we
>>> print(big)
use. A function takes
w
some input and
produces an output.

'Hello world' max() 'w'


(a string) function (a string)

Guido wrote this code


Max Function
A function is some
>>> big = max('Hello world') stored code that we
>>> print(big)
use. A function takes
w
some input and
def max(inp):
produces an output.
blah
'Hello world' blah 'w'
for x in inp:
(a string) blah
(a string)
blah

Guido wrote this code


Type Conversions
>>> print(float(99) / 100)
• When you put an integer 0.99
>>> i = 42
and floating point in an >>> type(i)
expression, the integer <class 'int'>
is implicitly converted to >>> f = float(i)
>>> print(f)
a float 42.0
>>> type(f)
• You can control this with <class 'float'>
the built-in functions int() >>> print(1 + 2 * float(3) / 4 – 5)
and float() -2.5
>>>
String >>> sval = '123'
>>> type(sval)
<class 'str'>
Conversions >>> print(sval + 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
• You can also use int() TypeError: cannot concatenate 'str'
and 'int'
and float() to convert >>> ival = int(sval)
between strings and >>> type(ival)
<class 'int'>
integers >>> print(ival + 1)
124
• You will get an error if the >>> nsv = 'hello bob'
>>> niv = int(nsv)
string does not contain Traceback (most recent call last):
numeric characters File "<stdin>", line 1, in <module>
ValueError: invalid literal for int()
Functions of Our Own…
Building our Own Functions
• We create a new function using the def keyword followed by
optional parameters in parentheses

• We indent the body of the function

• This defines the function but does not execute the body of the
function

def print_lyrics():
print("I'm a lumberjack, and I'm okay.")
print('I sleep all night and I work all day.')
print("I'm a lumberjack, and I'm okay.")
print_lyrics(): print('I sleep all night and I work all day.')

x = 5
print('Hello')

def print_lyrics():
print("I'm a lumberjack, and I'm okay.") Hello
print('I sleep all night and I work all day.')
Yo
print('Yo') 7
x = x + 2
print(x)
Definitions and Uses
• Once we have defined a function, we can call (or invoke) it
as many times as we like

• This is the store and reuse pattern


x = 5
print('Hello')

def print_lyrics():
print("I'm a lumberjack, and I'm okay.")
print('I sleep all night and I work all day.')

print('Yo')
print_lyrics()
x = x + 2
Hello
print(x) Yo
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
7
Arguments
• An argument is a value we pass into the function as its input
when we call the function

• We use arguments so we can direct the function to do different


kinds of work when we call it at different times

• We put the arguments in parentheses after the name of the


function
big = max('Hello world')
Argument
Parameters
>>> def greet(lang):
... if lang == 'es':
A parameter is a variable which ... print('Hola')
... elif lang == 'fr':
we use in the function definition. ... print('Bonjour')
It is a “handle” that allows the ... else:
... print('Hello')
code in the function to access ...
>>> greet('en')
the arguments for a particular Hello
function invocation. >>> greet('es')
Hola
>>> greet('fr')
Bonjour
>>>
Return Values
Often a function will take its arguments, do some computation, and
return a value to be used as the value of the function call in the
calling expression. The return keyword is used for this.

def greet():
return "Hello" Hello Glenn
Hello Sally
print(greet(), "Glenn")
print(greet(), "Sally")
Return Value
>>> def greet(lang):
... if lang == 'es':
• A “fruitful” function is one ... return 'Hola'
... elif lang == 'fr':
that produces a result (or ... return 'Bonjour'
return value) ... else:
... return 'Hello'
...
• The return statement ends >>> print(greet('en'),'Glenn')
the function execution and Hello Glenn
>>> print(greet('es'),'Sally')
“sends back” the result of Hola Sally
the function >>> print(greet('fr'),'Michael')
Bonjour Michael
>>>
Arguments, Parameters, and
Results
>>> big = max('Hello world') Parameter
>>> print(big)
w
def max(inp):
blah
blah
'Hello world' for x in inp: 'w'
blah
blah
Argument return 'w'
Result
Multiple Parameters / Arguments
• We can define more than one
parameter in the function def addtwo(a, b):
definition added = a + b
return added
• We simply add more arguments
x = addtwo(3, 5)
when we call the function print(x)

• We match the number and order 8


of arguments and parameters
Void (non-fruitful) Functions

• When a function does not return a value, we call it a “void”


function

• Functions that return values are “fruitful” functions

• Void functions are “not fruitful”


To function or not to function...
• Organize your code into “paragraphs” - capture a complete
thought and “name it”

• Don’t repeat yourself - make it work once and then reuse it

• If something gets too long or complex, break it up into logical


chunks and put those chunks in functions

• Make a library of common stuff that you do over and over -


perhaps share this with your friends...
Summary
• Functions • Arguments
• Built-In Functions • Results (fruitful functions)
• Type conversion (int, float) • Void (non-fruitful) functions
• String conversions • Why use functions?
• Parameters
Exercise

Rewrite your pay computation with time-and-a-


half for overtime and create a function called
computepay which takes two parameters ( hours
and rate).

Enter Hours: 45
Enter Rate: 10

Pay: 475.0
475 = 40 * 10 + 5 * 15
n=5 Repeated Steps
Output:
No Yes Program:
n>0? 5
n = 5 4
print(n) while n > 0 :
3
print(n)
n = n – 1 2
n = n -1 print('Blastoff!') 1
print(n) Blastoff!
0
Loops (repeated steps) have iteration variables that
print('Blastoff') change each time through a loop. Often these iteration
variables go through a sequence of numbers.
n=5 An Infinite Loop
No Yes
n>0?
n = 5
while n > 0 :
print('Lather')
print('Lather')
print('Rinse')
print('Rinse') print('Dry off!')

print('Dry off!') What is wrong with this loop?


n=0 Another Loop
No Yes
n>0?
n = 0
while n > 0 :
print('Lather')
print('Lather')
print('Rinse')
print('Rinse') print('Dry off!')

print('Dry off!') What is this loop doing?


Breaking Out of a Loop
• The break statement ends the current loop and jumps to the
statement immediately following the loop

• It is like a loop test that can happen anywhere in the body of the
loop
while True: > hello there
line = input('> ') hello there
if line == 'done' : > finished
break finished
print(line) > done
print('Done!') Done!
Breaking Out of a Loop
• The break statement ends the current loop and jumps to the
statement immediately following the loop

• It is like a loop test that can happen anywhere in the body of the
loop
while True: > hello there
line = input('> ') hello there
if line == 'done' : > finished
break finished
print(line) > done
print('Done!') Done!
No Yes
while True: True ?
line = input('> ')
if line == 'done' :
....
break
print(line)
print('Done!')
break

...

print('Done')
Finishing an Iteration with
continue
The continue statement ends the current iteration and jumps to the
top of the loop and starts the next iteration

while True:
> hello there
line = input('> ')
if line[0] == '#' : hello there
continue > # don't print this
if line == 'done' : > print this!
break print this!
print(line) > done
print('Done!') Done!
Finishing an Iteration with
continue
The continue statement ends the current iteration and jumps to the
top of the loop and starts the next iteration

while True:
> hello there
line = input('> ')
if line[0] == '#' : hello there
continue > # don't print this
if line == 'done' : > print this!
break print this!
print(line) > done
print('Done!') Done!
No
True ? Yes
while True:
line = raw_input('> ') ....
if line[0] == '#' :
continue
if line == 'done' : continue
break
print(line)
...
print('Done!')

print('Done')
Indefinite Loops
• While loops are called “indefinite loops” because they keep
going until a logical condition becomes False

• The loops we have seen so far are pretty easy to examine to see
if they will terminate or if they will be “infinite loops”

• Sometimes it is a little harder to be sure if a loop will terminate


Definite Loops
Iterating over a set of items…
Definite Loops
• Quite often we have a list of items of the lines in a file -
effectively a finite set of things

• We can write a loop to run the loop once for each of the items in
a set using the Python for construct

• These loops are called “definite loops” because they execute an


exact number of times

• We say that “definite loops iterate through the members of a set”


A Simple Definite Loop
5
for i in [5, 4, 3, 2, 1] :
print(i)
4
print('Blastoff!') 3
2
1
Blastoff!
A Definite Loop with Strings

Happy New Year: Joseph


friends = ['Joseph', 'Glenn', 'Sally'] Happy New Year: Glenn
for friend in friends : Happy New Year: Sally
print('Happy New Year:', friend)
print('Done!')
Done!
A Simple Definite Loop
Yes No
Done? Move i ahead 5
for i in [5, 4, 3, 2, 1] : 4
print(i) 3
print(i) print('Blastoff!')
2
1
Blastoff!

Definite loops (for loops) have explicit iteration variables


print('Blast off!') that change each time through a loop. These iteration
variables move through the sequence or set.
Looking at in...
• The iteration variable
“iterates” through the Five-element
sequence (ordered set) sequence
Iteration variable
• The block (body) of code is
executed once for each for i in [5, 4, 3, 2, 1] :
value in the sequence print(i)

• The iteration variable moves


through all of the values in
the sequence
No • The iteration variable “iterates”
Yes
Done? Move i ahead through the sequence (ordered
set)
print(i)
• The block (body) of code is
executed once for each value in
the sequence

• The iteration variable moves


through all of the values in the
for i in [5, 4, 3, 2, 1] :
print(i)
sequence
i=5
No print(i)
Yes
Done? Move i ahead i=4
print(i)
print(i)
i=3
print(i)

i=2

for i in [5, 4, 3, 2, 1] : print(i)


print(i)
i=1
print(i)
Loop Idioms:
What We Do in Loops

Note: Even though these examples are simple,


the patterns apply to all kinds of loops
Making “smart” loops
Set some variables to
initial values
for thing in data:
The trick is “knowing” something
Look for something or
about the whole loop when you
do something to each
are stuck writing code that only
entry separately,
sees one entry at a time updating a variable

Look at the variables


Looping Through a Set
$ python basicloop.py
print('Before')
Before
for thing in [9, 41, 12, 3, 74, 15] : 9
print(thing) 41
print('After')
12
3
74
15
After
What is the Largest Number?
What is the Largest Number?

3
What is the Largest Number?

41
What is the Largest Number?

12
What is the Largest Number?

9
What is the Largest Number?

74
What is the Largest Number?

15
What is the Largest Number?
What is the Largest Number?

3 41 12 9 74 15
What is the Largest Number?

largest_so_far -1
What is the Largest Number?

largest_so_far 3
What is the Largest Number?

41

largest_so_far 41
What is the Largest Number?

12

largest_so_far 41
What is the Largest Number?

largest_so_far 41
What is the Largest Number?

74

largest_so_far 74
What is the Largest Number?

15

74
What is the Largest Number?

3 41 12 9 74 15

74
Finding the Largest Value
$ python largest.py
largest_so_far = -1
Before -1
print('Before', largest_so_far)
for the_num in [9, 41, 12, 3, 74, 15] : 9 9
if the_num > largest_so_far : 41 41
largest_so_far = the_num 41 12
print(largest_so_far, the_num) 41 3
74 74
print('After', largest_so_far) 74 15
After 74

We make a variable that contains the largest value we have seen so far. If the current
number we are looking at is larger, it is the new largest value we have seen so far.
More Loop Patterns…
Counting in a Loop
$ python countloop.py
zork = 0 Before 0
print('Before', zork) 19
for thing in [9, 41, 12, 3, 74, 15] :
2 41
zork = zork + 1
print(zork, thing) 3 12
print('After', zork) 43
5 74
6 15
After 6

To count how many times we execute a loop, we introduce a counter variable


that starts at 0 and we add one to it each time through the loop.
Summing in a Loop
$ python countloop.py
zork = 0 Before 0
print('Before', zork) 99
for thing in [9, 41, 12, 3, 74, 15] :
50 41
zork = zork + thing
print(zork, thing) 62 12
print('After', zork) 65 3
139 74
154 15
After 154

To add up a value we encounter in a loop, we introduce a sum variable that


starts at 0 and we add the value to the sum each time through the loop.
Finding the Average in a Loop
$ python averageloop.py
count = 0 Before 0 0
sum = 0
199
print('Before', count, sum)
for value in [9, 41, 12, 3, 74, 15] : 2 50 41
count = count + 1 3 62 12
sum = sum + value 4 65 3
print(count, sum, value) 5 139 74
print('After', count, sum, sum / count) 6 154 15
After 6 154 25.666

An average just combines the counting and sum patterns and


divides when the loop is done.
Filtering in a Loop
print('Before') $ python search1.py
for value in [9, 41, 12, 3, 74, 15] : Before
if value > 20: Large number 41
print('Large number',value)
print('After')
Large number 74
After

We use an if statement in the loop to catch / filter the


values we are looking for.
Search Using a Boolean Variable
$ python search1.py
found = False
Before False
print('Before', found) False 9
for value in [9, 41, 12, 3, 74, 15] : False 41
if value == 3 : False 12
found = True True 3
print(found, value) True 74
print('After', found)
True 15
After True

If we just want to search and know if a value was found, we use a variable that
starts at False and is set to True as soon as we find what we are looking for.
How to Find the Smallest Value
$ python largest.py
largest_so_far = -1
Before -1
print('Before', largest_so_far)
for the_num in [9, 41, 12, 3, 74, 15] : 9 9
if the_num > largest_so_far : 41 41
largest_so_far = the_num 41 12
print(largest_so_far, the_num) 41 3
74 74
print('After', largest_so_far) 74 15
After 74

How would we change this to make it find the smallest value in the list?
Finding the Smallest Value
smallest_so_far = -1
print('Before', smallest_so_far)
for the_num in [9, 41, 12, 3, 74, 15] :
if the_num < smallest_so_far :
smallest_so_far = the_num
print(smallest_so_far, the_num)

print('After', smallest_so_far)

We switched the variable name to smallest_so_far and switched the > to <
Finding the Smallest Value
$ python smallbad.py
smallest_so_far = -1
Before -1
print('Before', smallest_so_far)
for the_num in [9, 41, 12, 3, 74, 15] : -1 9
if the_num < smallest_so_far : -1 41
smallest_so_far = the_num -1 12
print(smallest_so_far, the_num) -1 3
-1 74
print('After', smallest_so_far) -1 15
After -1

We switched the variable name to smallest_so_far and switched the > to <
Finding the Smallest Value
smallest = None $ python smallest.py
print('Before') Before
for value in [9, 41, 12, 3, 74, 15] : 99
if smallest is None :
9 41
smallest = value
elif value < smallest : 9 12
smallest = value 33
print(smallest, value) 3 74
print('After', smallest) 3 15
After 3

We still have a variable that is the smallest so far. The first time through the loop
smallest is None, so we take the first value to be the smallest.
The is and is not Operators
• Python has an is operator
smallest = None that can be used in logical
print('Before') expressions
for value in [3, 41, 12, 9, 74, 15] :
if smallest is None :
• Implies “is the same as”
smallest = value
elif value < smallest :
smallest = value • Similar to, but stronger than
print(smallest, value) ==
print('After', smallest)
• is not also is a logical
operator
Summary
• While loops (indefinite) • For loops (definite)
• Infinite loops • Iteration variables
• Using break • Loop idioms
• Using continue • Largest or smallest
• None constants and variables

You might also like