Loops and Escapes


Topics:

  • For Loops
  • Using range() in a loop
  • Nested for loops for complex data structures
  • Indexing loops with enumerate()
  • Loops with zip()
  • While loops
  • Loop control and escapes with continue and break

Introduction

Thus far, everything we've done we could do by hand just as easily, if not more so. The really great thing about computers is their ability to do things over and over again really quickly, without getting bored. It's easy for us to do stuff on a handful of data points, but more than a couple dozen and we'd really start wishing the computer was doing it for us. Every programming language has a way to do this, looping over a section of code multiple times. Python has two kinds of loops: the for loop and the while loop.

In principle, anything you can do with one you should be able to do with the other somehow, but it's often a lot cleaner to pick the appropriate one for the situation. Hopefully once you've seen how each one works, it will be reasonably obvious which one is best for any given job at hand.


for Loops



The for loop allows you to perform the same actions multiple times, but with different data each time through (some languages have an equivalent construct and call it a for each loop). We've even used this concept before when talking about lists, we just didn't explain what was going on:

#!/usr/bin/python
 
li = ['Red Leicester', 'Gruyere', 'Camembert', 'Parmesan', 'Mozarella', 'Cheddar']
 
#iterate over list
for x in li: print x

$./loops.py
Red Leicester
Gruyere
Camembert
Parmesan
Mozarella
Cheddar

The general syntax here is that for goes through each item in the list (or tuple or dictionary) after the in, stores that in the variable name before in, then executes the code after the colon. Once it's done all that code, it gets a new element from the list, and repeats.

for VARIABLE_NAME in CONTAINER:
    DO_SOMETHING
    DO_SOMETHING_ELSE
    ....
 
# is the same as
 
VARIABLE_NAME = CONTAINER[0]
DO_SOMETHING
DO_SOMETHING_ELSE
 
VARIABLE_NAME = CONTAINER[1]
DO_SOMETHING
DO_SOMETHING_ELSE
 
VARIABLE_NAME = CONTAINER[2]
DO_SOMETHING
DO_SOMETHING_ELSE
...
# and so on

Just like an if statement, if you want to do more than one thing inside the loop, you can start a new block of indented lines after the colon, and then when you're done with the code you want to run every time, go back to the original indentation:

#!/usr/bin/python
 
li = ['Red Leicester', 'Gruyere', 'Camembert', 'Parmesan', 'Mozarella', 'Cheddar']
 
#iterate over list
for x in li:
    print "Cleese: Do you have any %s?" % x
    if x == 'Camembert':
        print "Chapman: Oooh, the cat's eaten it!"
    else:
        print "Chapman: No."
print "Cleese: Well I'm sorry, but I'm going to have to shoot you."

Looping a given number of times


for x in range(4):
    print 'hello'
y = range(4)
print y

$./loops.py
hello
hello
hello
hello
[0, 1, 2, 3]

Looping over fancy data structures

You are also allowed to loop over a dictionary. Be aware that it loops over the keys of the dictionary (i.e. the ones you put in brackets to get the values), and that you shouldn't count on any particular ordering to those keys:

knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for x in knights:
    print x
    print knights[x]
$./loops.py
gallahad
the pure
robin
the brave

It turns out that you can put just about any valid Python code you want inside of a for loop. That happens to include other for loops! This is really useful for our fancy nested structures:

liLi = [[1,2,3],[7,8,9]]
for x in liLi:
    print x
    for y in x:
        print y
    print

$./loops.py
[1, 2, 3]
1
2
3
 
[7, 8, 9]
7
8
9

Mutating lists with loops


When we were discussing lists, we said that they were mutable. We showed that in a couple of ways, such as by using the sort() method. If we wanted to change elements in a list, here's one way we might think to do it:

li2 = [1,22,48,36,101]
print li2
for x in li2:
    x = x + 42
print li2

$./loops.py
[1, 22, 48, 36, 101]
[1, 22, 48, 36, 101]

The reason this doesn't work as you might expect is actually fairly subtle, and is all to do with yesterday's informative interlude about names and references. If that didn't make sense to you, let's just say that you usually have to modify the list directly, rather than the loop variable:

li2 = [1,22,48,36,101, 42]
print li2
for x in range( len(li2) ):
    li2[x] = li2[x] + 23
print li2

Here we've used the range() function to make a list of indexes as long as the list, li2. This is admittedly clunky, and Python has a built-in alternative, enumerate().

enumerate() returns a tuple with the index of the item as well as the item itself. Because of the way Python handles assignment of tuples, you can then assign that tuple to two variables, one for the index and one for the element itself. You can use the index to access the actual item in the list, not just the copy. However, you can access the copy, too, if you want.


print li2
for xInd, x in enumerate(li2):
    print x
    li2[xInd] = li2[xInd] + 23
    print x
    print
print li2

$./loops.py
[1, 22, 48, 36, 101, 42]
1
1
 
22
22
 
48
48
 
36
36
 
101
101
 
42
42
 
[24, 45, 71, 59, 124, 65]


Looping over multiple lists at the same time


You may sometimes need to loop over multiple lists at the same time. The easiest way to do that is to use the zip function, which returns a tuple containing the item corresponding items in each list.
for x,y,z in zip(li,li2,li):
    print x,y,z

$./loops.py
Red Leicester 1 Red Leicester
Gruyere 22 Gruyere
Camembert 48 Camembert
Parmesan 36 Parmesan
Mozarella 101 Mozarella
Cheddar 42 Cheddar

while loops


The rough format of a while loop is similar to a for loop: there's a colon at the end of the first line, and an indented block of code that gets run every time through. Despite this general similarity, while acts a little differently than a for loop. Instead of giving a list of items to iterate over, a while loop continues until the statement between while and the colon no longer has thruthiness (e.g. False, (), [], '', etc).

#!/usr/bin/python
 
x = 'Ni!'
while x:
    print x
    x = x[1:]
print
 
x = 5
while x > 0:
    print x
    x = x - 1
print

$./loops.py
Ni!
i!
!
 
5
4
3
2
1

This morning, some of you asked "what if the second time we put something into the gene finder it's still not the right length?" This is a perfect time to use a while loop:

hexamer = ""
 
while len(hexamer) != 6:
    hexamer = raw_input("Please input a six-character sequence: ")


Escaping Loops


Occasionally, you might want to get out of a loop before the truth statement is met (with a while loop) or you've gone through every element (with a for loop). In fact, some loops are designed such that the control condition at the top of the loop is never met! You can modify the default flow of the loop using break and continue. The keyword break ends the loop right where you are, while the keyword continue goes back to the top of the loop (bringing in the next item in the list if it's a for loop).

while True:
    dividend = int(raw_input("Dividend: "))
    divisor = int(raw_input("Divisor: "))
    if divisor == 0:
        break
    print dividend/divisor

#!/usr/bin/python
 
x = 10
while x:
    x = x-1
    #use mod to check if number
    #is even?  go to next number
    if x % 2 == 0:
        continue
    #use comma to print multiple
    #things on same line
    print x,
print

$./loops.py
9 7 5 3 1

In this example, each number is checked by the if statement to see if it is even. In the event the number is even, the loop goes back to the while logical expression (i.e. that x != 0) and continues.

You can use else to check whether a for or while loop finished normally, without hitting a break. Just like an if statement, the else should be at the same level of indentation as the for or while, have a colon, and then a block of code to run.

#!/usr/bin/python
 
y = 10
x = y-1
while x > 1:
    if y % x == 0:
        print x, 'is a factor of', y
        break
    x = x-1
else:
    print y, 'is prime'

$ ./loops.py
5 is a factor of 10
Here, y is checked for each number to see if it is evenly divisible (also called a factor). If y is evenly divisible, the number is printed and the code stops. If no factor is found, the loop defers to the else statement. What happens when you change the y to 100? 43? 3?



List Comprehensions

Sometimes we'll want to change every item in a list in a systematic way. For example, we may want to add one to each of a list of integers:

#!/usr/bin/python
 
a = range(10)
b = []
for i in a:
    b.append(i + 1)
a = b
print a



This is a totally acceptable way to solve this problem. However, because it is very common to commit the same operation on every member of a list, Python provides for us a shortcut that is both syntactically more concise, and computationally optimized to be much, much faster. It's called list comprehension, and it works like this:

#!/usr/bin/python
 
# let's recreate our list
a = range(10)
# note that the entire statement below is contained in **[ ]**
# This is the defining syntax of a **list comprehension**.
print [ i + 1 for i in a ]
 
# note also that we have to save the resulting new list back
# to **a** if we want to replace the old list
a = [ i + 1 for i in a ]
print a
Okay, we'll only get a little bit fancier here, exploring the idea of a nested list comprehension instead of a nested for loop. If you wanted to make a list of lists, with 10 lists of integers ranging from 0 to 99, you might have to type it out like this:

# terrible way to do this
b = [ range(100), range(100), range(100), etc, etc, etc...]
 
Or you could use a nested for loop:
b = []
 
for i in range(10):
    b.append(range(100))
Not too bad, but we can use list comprehensions in a nested format as well, and make this even cleaner and faster:
b = [ range(100) for i in range(10) ]



Exercises


1) FizzBuzz
FizzBuzz is a counting game where you need to be a bit on your toes. You start counting upwards, but:
  • When you have a number that's a multiple of three, you say "Fizz"
  • When you have a number that's a multiple of five, you say "Buzz"
  • When you have a number that's a multiple of both three and five, you say "FizzBuzz"
  • Otherwise, you just say the number

Write a program that plays FizzBuzz for the numbers from 1 to 100. The output should look like this:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...

You may find the % (modulo) operator helpful. This gives you the remainder when you divide one number by another, so:
  • 1 % 3 gives 1
  • 2 % 3 gives 2
  • 3 % 3 gives 0
  • 4 % 3 gives 1
  • 5 % 3 gives 2
  • 6 % 3 gives 0
  • 7 % 3 gives 1 again, and so on.

2) Getting comfortable with loops (adapted from Learning Python)


a) Computers only really understand numbers, not letters. Therefore, letters and symbols are stored in the computer according to numbers. The American Standard Code for Information Interchange (ASCII) is a formalized code that links characters and numbers. You determine the ASCII number code of a letter using the built-in function ord(character).
For example,

>>> print ord('T')
84
Write a for loop that prints the ASCII code of each character in the string 'Rock and Roll'. HINTS: You can loop over a string the same way you loop over a list.
b) Next, change your loop to compute the sum of the ASCII codes of all characters in the string.
c) Modify your code to print a new list that contains the ASCII codes of the characters in the string.

3) All Roads Lead to Rome (adapted from Learning Python)


A coworker (who obviously is not a native Python speaker) hands you the following code:
#!/usr/bin/python
 
L = [1,2,4,8,16,32,64]
x = 5
 
found = i = 0
while not found and i < len(L):
    #check if 2 to the power
    #of x is in the list
    if 2 ** x == L[i]:
        found = 1
    else:
        i = i+1
if found:
    print 'at index', i
else:
    print x, 'not found'
$ ./powers.py
at index 5

As is, the script does not follow normal Python coding techniques. Follow the steps below to improve it.
a) Rewrite this code with a while/else loop to eliminate the found flag and the final if statement.
b) Rewrite the example to use a for/else loop to eliminate the explicit list indexing logic.
c) Remove the loop completely by rewriting the examples using an expression with in (HINT: try the line 'print 2 in [1,2,3]')
d) Use a for loop and the list append method to generate the list L instead of typing it by hand.
e) (Bonus) Use a list comprehension and the ** operator to generate the list L in one line.** is exponentiation, so 10**3 is 1000.


4) List comprehension Practice

a) Using a for loop, convert a list of 10,000,000 integers to floating point values. Pay attention to how long it takes by counting the seconds in your head.

b) Now, code the same conversion with a list comprehension. Again, count the time in your head.

c) Next, create a smaller list of 100 integers, and use list comprehension to double the value at each position in the list.

5) Zippity Dictionaries


This morning, some of you approached Exercise #6 (our FASTA parser) with a strategy that first constructed two lists with matching indices, and then wanted to combine them into a dictionary. You likely found this (along with the rest of the exercise) to contain a tedious task of copying each list index into its position as a key or value in the dictionary. You can also use zip() to construct a dictionary from two lists.

# list of 0 to 10
a = range(10)
 
# list of 10 to 20
b = range(20)[10:]
 
d = zip(a,b)
 
print d
{0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15, 6: 16, 7: 17, 8: 18, 9: 19}

Now use these lists, with a for loop, and zip() to create a dictionary keyed by items from a, with values from b



6) Doing something interesting
Next week, we'll be doing a project on HIV, and will be looking at evolution at evolution in capsid epitopes. Let's get an early start by generating some simple summary statistics of the protein. Write a script that will tell you what percent of the structure is helical, beta sheet, or some other structure.

Here are some things to help you out:
A) Download the structure of Hexameric HIV-1 CA (PDB code 3GV2) as an example structure
B) I am supplying you with a script that will open the pdb file, parse out the information about the sequence and secondary structure, and save three lists called full_seq, helix_aa and sheet_aa. full_seq is a list containing the full sequence of the protein (NOTE: The protein crystallized as a homohexamer). helix_aa and sheet_aa are lists of secondary structure descriptions, which have the following formats when converted into lists (paraphrased from PDB file format documenation)

helix_aa
0. Record name 'HELIX'
1. Serial number of helix
2. Helix identifier
3. Name of initial residue
4. Chain identifier
5. Sequence number of initial residue
6. Name of terminal residue
7. Chain identifier
8. Sequence number of terminal residue

sheet_aa
0. Record name 'SHEET'
1. Strand number
2. Sheet identifier
3. Number of strands in sheet
4. Residue name of initial residue
5. Chain identifier of initial residue
6. Sequence number of initial residue
7. Residue name of terminal residue
8. Chain identifier of terminal residue
9. Sequence number of terminal residue

#!/usr/bin/python
##SCRIPT TO PARSE OUT SECONDARY STRUCTURE INFORMATION
 
import sys, os
 
full_seq = []
helix_aa = []
sheet_aa = []
 
f1 = open('3GV2.pdb' ,'r')
for next in f1:
    tmp = next.strip().split()
    if tmp[0] == 'SEQRES':
        if tmp[2] == 'A':
            full_seq.extend(tmp[4:])
    elif tmp[0] == 'HELIX':
        try:
            int(tmp[5])
        except:
            tmp[5] = tmp[5][:-1]
        helix_aa.append(tmp[:9])
    elif tmp[0] == 'SHEET':
        sheet_aa.append(tmp[:10])


ANSWER:
Total number of residues=342
Percent helical=45.029240
Percent B sheet=1.754386
Percent other=53.216374


BONUS: What is the average b-factor (measure of the amount of vibrational motion each atom is undergoing) for each region?

To help you, here is a modification of the script above that collects the information about each atom and stores them in the list atoms. Each atom has the following format when converted into list (paraphrased from PDB file format documenation)

atoms
0. Record name ATOM
1. Atom sequence number
2. Atom name
3. Residue name
4. Chain identifier
5. Residue sequence number
6-8. X, Y, Z coordinates
9. Occupancy in structure (1.0 = 100% occupied, 0.5 = 50% occupied)
10. B-factor
11. Element

#!/usr/bin/python
##SCRIPT TO PARSE OUT SECONDARY STRUCTURE AND ATOM INFORMATION
 
full_seq = []
helix_aa = []
sheet_aa = []
atoms = []
f1 = open('3GV2.pdb' ,'r')
for next in f1:
    tmp = next.strip().split()
    if tmp[0] == 'SEQRES':
        if tmp[2] == 'A':
            full_seq.extend(tmp[4:])
    elif tmp[0] == 'HELIX':
        try:
            int(tmp[5])
        except:
            tmp[5] = tmp[5][:-1]
        helix_aa.append(tmp[:9])
    elif tmp[0] == 'SHEET':
        sheet_aa.append(tmp[:10])
    elif tmp[0] == 'ATOM':
        if len(tmp) < 12:
            begin = tmp[0:2]
            end = tmp[3:]
            middle = [tmp[2][:3], tmp[2][4:]]
            tmp = begin + middle + end
        try:
            int(tmp[5])
        except:
            continue
        atoms.append(tmp)

ANSWER:
There are 342 residues in the sequence
    Helix    Sheet    Other
A     42.75    35.20    44.31
C     42.75    35.20    44.31
B     42.75    35.20    44.31
E     42.75    35.20    44.31
D     42.75    35.20    44.31
F     42.75    35.20    44.31

Note that this likely means the six monomers were simply one monomer repeated.

7) The Sieve of Eratosthenes


The sieve of Eratosthenes is a very nice way to find prime numbers and also a very nice way to use practically all the logic we have learned this afternoon. The basic algorithm goes like this:

To find all the prime numbers less than or equal to a given integer n by Eratosthenes' method:
  1. Create a list of consecutive integers from two to n: (2, 3, 4, ..., n).
  2. Initially, let p equal 2, the first prime number.
  3. Strike from the list all multiples of p less than or equal to n. (2p, 3p, 4p, etc.)
  4. Find the first number remaining on the list after p (this number is the next prime); replace p with this number.
  5. Repeat steps 3 and 4 until p2 is greater than n.
  6. All the remaining numbers in the list are prime.

Implement the sieve to find all the prime numbers smaller than 100.

Solutions

FizzBuzz

Here's one way to do this:

nums = [i+1 for i in range(100)]
 
for x in nums:
    if (x % 3 == 0) and (x % 5 == 0):
        print "FizzBuzz"
    elif x % 3 == 0:
        print "Fizz"
    elif x % 5 == 0:
        print "Buzz"
    else:
        print x

Rock 'n' Roll



#!/usr/bin/env python
 
rockString = 'Rock and Roll'
print rockString
print
 
# a)
print 'a)'
for x in rockString: print 'The ASCII value of %s is %s' % (x, ord(x))
print
 
# b)
print 'b)'
sum = 0
for x in rockString: sum = ord(x) + sum
print sum, '''is the sum of the ASCII values corresponding to each of the letters of the string 'Rock and Roll'.'''
print
 
# c)
print 'c)'
rockASCIIlist = [ ]
for xInd, x in enumerate(rockString):
    rockASCIIlist.append([ ])
    print rockASCIIlist
    rockASCIIlist[xInd] = ord(x)
    # print rockASCIIlist # uncomment this to see the list grow with each iteration
print rockASCIIlist
print

All Roads Lead to Rome



#!/usr/bin/env python
 
# a)
print 'a)'
L = [1,2,4,8,16,32,64]
x = 5
 
# Less convoluted, but still indexing
i = 0
while i < len(L):
    if 2 ** x == L[i]:
        print 'at index', i
        break
    else:
        i = i+1
else:
    print x, 'not found'
print
 
# b)
print 'b)'
L = [1,2,4,8,16,32,64]
x = 5
for yInd, y in enumerate(L):
    if 2 ** x == y:
        print 'at index %s' % (yInd)
        break
print
 
# c)
print 'c)'
# Quickest way to see if something is in a list
print 2 in [1,2,3]
L = [1,2,4,8,16,32,64]
x = 5
print 2**x in L
print
 
# d)
 
L = []
for x in range(7):
    L.append(2 ** x)
print L
print
 

List Comprehension Practice


#!/usr/bin/env python
 
# a)
print 'a)'
print
L = []
for x in range(10000):
    L.append(x+1)
    # print type(x) # uncomment to see that all the numbers are ints
for xInd, x in enumerate(L): # note that we have to employ the enumerate() method if we want to actually change the list!
    L[xInd] = float(x)
    # print type(x) # uncomment to see that all the numbers are now floats
print L
print
 
# b)
print 'b)'
print
L = range(10000)
L = [float(i) for i in L]
print L
print
print 'The list is comprised of %s.' % (type(L[1])) # uncomment to see that the numbers are floats
print
 
# c)
print 'c)'
print
L = range(100)
print L
L = [i*2 for i in L]
print L
 

Zippity Dictionaries



#!/usr/bin/env python
 
# list of 0 to 10
a = range(10)
print a
print
 
# list of 10 to 20
b = range(20)[10:]
print b
print
 
d = {}
for x,y in zip(a,b):
    d[x] = y
print dict
print
 
#### OR ####
 
d = dict(zip(a,b))
 
 


Doing Something Interesting



#!/usr/bin/env python
 
#### The code below was provided for you ####
import sys, os
 
full_seq = []
helix_aa = []
sheet_aa = []
 
f1 = open('3GV2.pdb' ,'r')
for next in f1:
    tmp = next.strip().split()
    if tmp[0] == 'SEQRES':
        if tmp[2] == 'A':
            full_seq.extend(tmp[4:])
    elif tmp[0] == 'HELIX':
        try:
            int(tmp[5])
        except:
            tmp[5] = tmp[5][:-1]
        helix_aa.append(tmp[:9])
    elif tmp[0] == 'SHEET':
        sheet_aa.append(tmp[:10])
#### The code above was provided for you ####
 
HelicalResidues = 0
for helix_inst in helix_aa:
    # verify that the helix is part of chain A, since this
    # particular protein crystallized as a dimer and we
    # only care about the number of helical residues per
    # polypeptide
    if helix_inst[4] == 'A' and helix_inst[7] == 'A':
        # Add to a running total of the number of helical
        # residues by adding to the total the difference
        # between the residue number of the last amino acid and the
        # residue number of the first amino acid
        HelicalResidues += (float(helix_inst[8]) - float(helix_inst[5]) + 1)
 
# This following code will do the same for beta sheet residues
# as we did above for helical residues
BetaSheetResidues = 0
for sheet_inst in sheet_aa:
    if sheet_inst[5] == 'A' and sheet_inst[8] == 'A':
        BetaSheetResidues += (float(sheet_inst[9]) - float(sheet_inst[6]) + 1)
 
seq_len = len(full_seq)
 
print "Total number of residues=%d" % seq_len
print "Percent helical=%f" % ((HelicalResidues/seq_len) * 100)
print "Percent B sheet=%f" % ((BetaSheetResidues/seq_len) * 100)
print "Percent other=%f" % (((seq_len - HelicalResidues - BetaSheetResidues)/seq_len) * 100)
 

import sys, os
 
full_seq = []
helix_aa = []
sheet_aa = []
atoms = []
f1 = open('3GV2.pdb' ,'r')
for next in f1:
    tmp = next.strip().split()
    if tmp[0] == 'SEQRES':
        if tmp[2] == 'A':
            full_seq.extend(tmp[4:])
    elif tmp[0] == 'HELIX':
        try:
            int(tmp[5])
        except:
            tmp[5] = tmp[5][:-1]
        helix_aa.append(tmp[:9])
    elif tmp[0] == 'SHEET':
        sheet_aa.append(tmp[:10])
    elif tmp[0] == 'ATOM':
        if len(tmp) < 12:
            begin = tmp[0:2]
            end = tmp[3:]
            middle = [tmp[2][:3], tmp[2][4:]]
            tmp = begin + middle + end
        try:
            int(tmp[5])
        except:
            continue
        atoms.append(tmp)
######################
# My code starts here
######################
num_helix_res = 0.0
print "There are %s residues in the sequence" % len(full_seq)
 
# Set up a listing of features by residue, then fill it in as we go along
feature = ['Other']*(10000)
 
for aa in helix_aa:
    # We add 1 because there are b-a+1 residues between a and b, inclusive
    num_helix_res += float(aa[8]) - float(aa[5]) + 1
    for i in range(int(aa[5]), int(aa[8])+1):
        feature[i] = 'Helix'
 
num_sheet_res = 0.0
for sheet in sheet_aa:
    num_sheet_res += float(sheet[9]) - float(sheet[6]) + 1
    for i in range(int(sheet[6]), int(sheet[9])+1):
        feature[i] = 'Sheet'
 
 
 # atom[4] == chain id
 # atom[5] == residue #
 # atom[10] == b-factor
 
 
helix_bfactors = {}
sheet_bfactors = {}
other_bfactors = {}
 
for atom in atoms:
    Chain = atom[4]
    BFactor = float(atom[10])
    ResidueNum = int(atom[5])
 
    if feature[ResidueNum] == 'Helix':
        if Chain not in helix_bfactors:
            helix_bfactors[Chain] = []
        helix_bfactors[Chain].append(BFactor)
    elif feature[ResidueNum] == 'Sheet':
        if Chain not in sheet_bfactors:
            sheet_bfactors[Chain] = []
        sheet_bfactors[Chain].append(BFactor)
    else:
        if Chain not in sheet_bfactors:
            other_bfactors[Chain] = []
        other_bfactors[Chain].append(BFactor)
 
for chain in helix_bfactors:
    # I could have used any of the different bfactor listings
    avg_helix = sum(helix_bfactors[chain])/len(helix_bfactors[chain])
    avg_sheet = sum(sheet_bfactors[chain])/len(sheet_bfactors[chain])
    avg_other = sum(other_bfactors[chain])/len(other_bfactors[chain])
    print '%s\t %5f\t %5f\t %5f' % (chain, avg_helix, avg_sheet, avg_other)
 

Sieve of Eratosthenes


nums = range(100)[2:]
 
idx = 0
 
while idx < len(nums):
    # Note that we're using a copy of the list for this for loop
    p = nums[idx]
    for prod in nums[idx+1:]:
        # If prod is divisible by p, remove it from the original list
        if prod % p == 0:
            nums.remove(prod)
 
    # At this point, the next number in nums should be another prime
    idx += 1
 
print nums