Actions

Difference between revisions of "Solving problems with Python"

From AstroEd

(Iteration)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
Now with many useful tools in hand, let us see how to make them work together to solve problems.
+
Now with many useful tools in hand from our [http://prancer.physics.louisville.edu/astrowiki/index.php/Python_for_Physics_and_Astronomy short course on using Python in Physics and Astronomy], let us see how to make them work together to solve problems.
  
  
Line 14: Line 14:
 
   pass
 
   pass
 
  elif x == 0:
 
  elif x == 0:
   print 'Cannot divide by zero.'
+
   print ('Cannot divide by zero.')
 
   exit()
 
   exit()
 
  else:
 
  else:
Line 22: Line 22:
 
Notice that indentation (by any fixed number of spaces) is used to separate the functions within the statement, and that each branch is defined by a '':''.  The end of a branch occurs when the indentation goes back to the previous level. Each decision is based on a logical ''boolean'' value such as (x > 0.), which is True when x is greater than 0. and False otherwise.  Within the if processing,  a ''pass'' is a way to do nothing, and an exit() leaves the entire program.
 
Notice that indentation (by any fixed number of spaces) is used to separate the functions within the statement, and that each branch is defined by a '':''.  The end of a branch occurs when the indentation goes back to the previous level. Each decision is based on a logical ''boolean'' value such as (x > 0.), which is True when x is greater than 0. and False otherwise.  Within the if processing,  a ''pass'' is a way to do nothing, and an exit() leaves the entire program.
  
A ''while'' statement tests whether its argument is true, and sets up a loop that continues as long as it is.  Program
+
A ''while'' statement tests whether its argument is true, and sets up a loop that continues as long as it is.  The program
 
    
 
    
 
  flag = True
 
  flag = True
Line 30: Line 30:
 
   if x > 10.:
 
   if x > 10.:
 
     flag = False
 
     flag = False
  print x
+
  print (x)
  
 
increases x until it is 11. and then prints the value.
 
increases x until it is 11. and then prints the value.
Line 41: Line 41:
 
     break
 
     break
 
   except ValueError:
 
   except ValueError:
  print "Oops!  That was no valid number.  Try again..."
+
    print ("Oops!  That was no valid number.  Try again...")
 
  y=x**2
 
  y=x**2
  print y
+
  print (y)
  
 
Here a ''break'' exits the loop from the ''try'' block unless an exception is thrown. A while statement can also test for something that is changed in the loop.
 
Here a ''break'' exits the loop from the ''try'' block unless an exception is thrown. A while statement can also test for something that is changed in the loop.
Line 49: Line 49:
 
== Functions ==
 
== Functions ==
  
Within a Python program you can define your own functions. Here's one to take an angle in degrees and reduce it an angle between 0 and 360.
+
Within a Python program you can define your own functions. Here's one to take an angle in degrees and reduce it to an angle between 0 and 360.
  
 
  def map360(angle):
 
  def map360(angle):
Line 72: Line 72:
  
 
  idata = range(10)
 
  idata = range(10)
  print idata
+
  print (idata)
 
  fdata = [-1.]
 
  fdata = [-1.]
 
  for x in idata:
 
  for x in idata:
 
   f = float(x)**2.0
 
   f = float(x)**2.0
 
   fdata.append(f)
 
   fdata.append(f)
   print fdata
+
   print (fdata)
  
 
generates this output
 
generates this output
Line 96: Line 96:
 
In the first line, range(10) creates a list with values from 0 to 9 which is the first line of the output.  We also made a floating point list with one value of -1. to start things off.  The ''for'' loop interated the idata list, and for each element (now labeled ''x'') we calculated x<sup>2.0</sup> and appended it to the fdata floating point list.  Inside the iterative loop we printed the list as it grew.
 
In the first line, range(10) creates a list with values from 0 to 9 which is the first line of the output.  We also made a floating point list with one value of -1. to start things off.  The ''for'' loop interated the idata list, and for each element (now labeled ''x'') we calculated x<sup>2.0</sup> and appended it to the fdata floating point list.  Inside the iterative loop we printed the list as it grew.
  
Lists and strings are ''iterable'', and so are dictionaries and even files. An example of iterating a dictionary would be
+
Lists and strings are ''iterable'', and so are dictionaries and even files. An example of iterating a dictionary through a list of its keys would be
  
 
  messier = {'1' : 'planetary nebula', '2' : 'globular cluster', '51' : 'spiral galaxy' }
 
  messier = {'1' : 'planetary nebula', '2' : 'globular cluster', '51' : 'spiral galaxy' }
  for key in messier:
+
  for key in messier.keys():
 
   if messier[key] == 'spiral galaxy':
 
   if messier[key] == 'spiral galaxy':
     print key
+
     print (key, messier[key])
  
 
which will print
 
which will print
  
  51
+
  51 spiral galaxy
  
 +
However, dictionaries can be interated for both key and value by looking at the full list of items.
  
The readline() function for a file iterates line by line through the file
+
messier = {'1' : 'planetary nebula', '2' : 'globular cluster', '51' : 'spiral galaxy' }
 +
for key, value in messier.items():
 +
  if value == 'spiral galaxy':
 +
    print (key, value)
 +
 
 +
 
 +
The readline() function for a file iterates line by line through the file, and
  
 
  myfile = open('catalog.dat')
 
  myfile = open('catalog.dat')
Line 115: Line 122:
 
   if not myline:
 
   if not myline:
 
     break
 
     break
   print line
+
   print (myline)
 
 
  
prints a line from ''catalog.dat''.  Subsequent readline()'s go through the file one line at a time. While
+
prints lines from ''catalog.dat''.  The simpler
  
  for line in open('catalog.dat')
+
  for myline in open('catalog.dat'):
   print line
+
   print (myline)
  
prints lines from the file one by one as iteratively read.  You can also open a file and read all the lines as a list
+
uses the iterative  for loop to print lines from the file one by one as read.  You can also open a file and read all the lines as a list and the iteration is hidden.
  
 
  myfile = open('catalog.dat')
 
  myfile = open('catalog.dat')
 
  mydata = myfile.readlines()
 
  mydata = myfile.readlines()
 
  myfile.close()
 
  myfile.close()
 +
 +
 +
== Iterables and generators ==
 +
 +
While on the topic of iteration, it is a good moment to explain the ideas of iterables and generators too.  The following is nearly verbatim from an excellent reply found on [https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do Stackoverflow], a really useful resource when you are lost.
 +
 +
When you create a list, you can read its items one by one. Reading its items one by one is called ''iteration''.  In Python 3 you would have
 +
 +
  mylist = [1, 2, 3]
 +
  for i in mylist:
 +
    print(i)
 +
 +
  1
 +
  2
 +
  3
 +
 +
Here "mylist" is iterable.  Everything you can use with  statement such "for... in..."  is an iterable.  This includes  lists, strings, and files.  Similarly, when you use a technique called "list comprehension" you can create a list in one line with an iterable
 +
 +
  mylist = [x*x for x in range(4)]
 +
 
 +
and see what you have by printing it one element at a time as we did above.
 +
 +
  for i in mylist:
 +
    print(i)
 +
 +
  0
 +
  1
 +
  4
 +
  9
 +
 +
This also shows how the "range(4)" function starts at 0 and ends at 3 in steps of 1. 
 +
 +
Iterables store their values in memory, and if you have a lot of values you might not want that.  ''Generators'' are an iterator that generates values as you go:
 +
 +
  mygenerator = (x*x for x in range(3))
 +
  for i in mygenerator:
 +
    print(i)
 +
 +
  0
 +
  1
 +
  4
 +
 +
The difference in syntax is subtle, the use if ( rather than [.  The results are similar, except you cannot ask "for i in mygenerator" more than once. 
 +
 +
 +
When defining a function to create a generator if you want to make many of them, you would use "yield" rather than "return" in the definition
 +
 +
  def createGenerator():
 +
    mylist = range(3)
 +
    for i in mylist:
 +
      yield i*i
 +
 +
  mygenerator = createGenerator()  # create a generator
 +
  print(mygenerator)                        # mygenerator is an object!
 +
 +
  for i in mygenerator:
 +
    print(i)
 +
 +
0
 +
1
 +
4
 +
 +
This example could be modified to return a huge set of values, but only once.  The "mygenerator" code returns the generator object, and it is run each time "for" uses the generator.  The first time the "for" calls the generator, an object is created from your function. It will run the code in your function from the beginning until it hits yield, then it will return the first value of the loop. Each subsequent call will run the loop you have written in the function one more time, and return the next value, until there is no value to return. The generator is considered empty once the function runs but does not hit yield anymore. This can happen if the loop comes to an end, or if it does not  satisfy another condition.
 +
 +
 +
  
 
== Examples ==
 
== Examples ==
 
   
 
   
 
For examples of Python illustrating flow control, functions, and iteration, see the [http://prancer.physics.louisville.edu/astrowiki/index.php/Python_examples examples] section.
 
For examples of Python illustrating flow control, functions, and iteration, see the [http://prancer.physics.louisville.edu/astrowiki/index.php/Python_examples examples] section.
 +
  
 
== Assignments ==
 
== Assignments ==
 
   
 
   
 
 
For the assigned homework to use these ideas,  see the [http://prancer.physics.louisville.edu/astrowiki/index.php/Python_assignments assignments] section.
 
For the assigned homework to use these ideas,  see the [http://prancer.physics.louisville.edu/astrowiki/index.php/Python_assignments assignments] section.

Latest revision as of 06:12, 21 February 2018

Now with many useful tools in hand from our short course on using Python in Physics and Astronomy, let us see how to make them work together to solve problems.


Flow control

The if statement is fundamental to making decisions within a program. It works simply

x=0.1
y=10.
z=0.
if x > 0.:
  y = 1./x
elif x < -1.:
  pass
elif x == 0:
  print ('Cannot divide by zero.')
  exit()
else:
  y = 1./x
z = y

Notice that indentation (by any fixed number of spaces) is used to separate the functions within the statement, and that each branch is defined by a :. The end of a branch occurs when the indentation goes back to the previous level. Each decision is based on a logical boolean value such as (x > 0.), which is True when x is greater than 0. and False otherwise. Within the if processing, a pass is a way to do nothing, and an exit() leaves the entire program.

A while statement tests whether its argument is true, and sets up a loop that continues as long as it is. The program

flag = True
x = 0.
while flag:
  x = x + 1.
  if x > 10.:
    flag = False
print (x)

increases x until it is 11. and then prints the value.

Loops such as this may include a try block. This enables handling an exception, such as in this program to calculate x2 with input from keyboard.

while True:
  try:
    x = int(raw_input("Please enter a number: "))
    break
  except ValueError:
    print ("Oops!  That was no valid number.  Try again...")
y=x**2
print (y)

Here a break exits the loop from the try block unless an exception is thrown. A while statement can also test for something that is changed in the loop.

Functions

Within a Python program you can define your own functions. Here's one to take an angle in degrees and reduce it to an angle between 0 and 360.

def map360(angle):
  if (angle < 0.0):   
    n = int(angle / 360.0) - 1
    return (angle - float(n) * 360.0)
  
  elif (angle >= 360.0):  
    n = int(angle / 360.0)
    return (angle - float(n) * 360.0)
  
  else:   
    return (angle)


Functions may have any number of objects as arguments, of any data type. Once defined, you may use a function anywhere in a program.

Iteration

The while loop makes repeated passes through a block of code as long as a test condition is satisfied. This allows us to make sequential changes to achieve a desired outcome, such as evaluating a series until the error is acceptable, or fitting data until the fitting errors are minimized. Python also has several built-in ways to manage interation, and the most useful is the for loop.

idata = range(10)
print (idata)
fdata = [-1.]
for x in idata:
  f = float(x)**2.0
  fdata.append(f)
  print (fdata)

generates this output

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[-1.0, 0.0]
[-1.0, 0.0, 1.0]
[-1.0, 0.0, 1.0, 4.0]
[-1.0, 0.0, 1.0, 4.0, 9.0]
[-1.0, 0.0, 1.0, 4.0, 9.0, 16.0]
[-1.0, 0.0, 1.0, 4.0, 9.0, 16.0, 25.0]
[-1.0, 0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0]
[-1.0, 0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0]
[-1.0, 0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0]
[-1.0, 0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0, 81.0]


In the first line, range(10) creates a list with values from 0 to 9 which is the first line of the output. We also made a floating point list with one value of -1. to start things off. The for loop interated the idata list, and for each element (now labeled x) we calculated x2.0 and appended it to the fdata floating point list. Inside the iterative loop we printed the list as it grew.

Lists and strings are iterable, and so are dictionaries and even files. An example of iterating a dictionary through a list of its keys would be

messier = {'1' : 'planetary nebula', '2' : 'globular cluster', '51' : 'spiral galaxy' }
for key in messier.keys():
  if messier[key] == 'spiral galaxy':
    print (key, messier[key])

which will print

51 spiral galaxy

However, dictionaries can be interated for both key and value by looking at the full list of items.

messier = {'1' : 'planetary nebula', '2' : 'globular cluster', '51' : 'spiral galaxy' }
for key, value in messier.items():
  if value == 'spiral galaxy':
    print (key, value)


The readline() function for a file iterates line by line through the file, and

myfile = open('catalog.dat')
while True:
  myline = myfile.readline()   
  if not myline:
    break
  print (myline)

prints lines from catalog.dat. The simpler

for myline in open('catalog.dat'):
  print (myline)

uses the iterative for loop to print lines from the file one by one as read. You can also open a file and read all the lines as a list and the iteration is hidden.

myfile = open('catalog.dat')
mydata = myfile.readlines()
myfile.close()


Iterables and generators

While on the topic of iteration, it is a good moment to explain the ideas of iterables and generators too. The following is nearly verbatim from an excellent reply found on Stackoverflow, a really useful resource when you are lost.

When you create a list, you can read its items one by one. Reading its items one by one is called iteration. In Python 3 you would have

 mylist = [1, 2, 3]
 for i in mylist:
   print(i)
 1
 2
 3

Here "mylist" is iterable. Everything you can use with statement such "for... in..." is an iterable. This includes lists, strings, and files. Similarly, when you use a technique called "list comprehension" you can create a list in one line with an iterable

 mylist = [x*x for x in range(4)]
 

and see what you have by printing it one element at a time as we did above.

 for i in mylist:
   print(i)
 0
 1
 4
 9

This also shows how the "range(4)" function starts at 0 and ends at 3 in steps of 1.

Iterables store their values in memory, and if you have a lot of values you might not want that. Generators are an iterator that generates values as you go:

 mygenerator = (x*x for x in range(3))
 for i in mygenerator:
   print(i)
 0
 1
 4

The difference in syntax is subtle, the use if ( rather than [. The results are similar, except you cannot ask "for i in mygenerator" more than once.


When defining a function to create a generator if you want to make many of them, you would use "yield" rather than "return" in the definition

 def createGenerator():
   mylist = range(3)
   for i in mylist:
     yield i*i
 mygenerator = createGenerator()  # create a generator
 print(mygenerator)                         # mygenerator is an object!
 for i in mygenerator:
   print(i)

0
1 
4

This example could be modified to return a huge set of values, but only once. The "mygenerator" code returns the generator object, and it is run each time "for" uses the generator. The first time the "for" calls the generator, an object is created from your function. It will run the code in your function from the beginning until it hits yield, then it will return the first value of the loop. Each subsequent call will run the loop you have written in the function one more time, and return the next value, until there is no value to return. The generator is considered empty once the function runs but does not hit yield anymore. This can happen if the loop comes to an end, or if it does not satisfy another condition.



Examples

For examples of Python illustrating flow control, functions, and iteration, see the examples section.


Assignments

For the assigned homework to use these ideas, see the assignments section.