Repeating things is what computers are good at and humans find boring! Repetition is controlled with loops, which come in two varieties. for… loops are when you want something to repeat a known number of times, whereas while… loops will run for an unspecified duration until some condition is met.


for… loops§

In many languages for is used for looping over some numbers, which you then use to index some other object (a string or an array).

Python loops are more flexible; you can loop over the contents directly without needing an index.

For instance, strings and lists both know how to ‘iterate’:

for thisLetter in 'hello':
print('loop done')

for thisInt in [1,2,3]:
    print(thisInt, thisInt*3)

for... loops§

In MATLAB, you might write:

subs = {'sub01' 'sub02' 'sub03'}
for i = 1:3
    sub = subs{i}

In Python this would be:

subs = ['sub01', 'sub01', 'sub03']
for sub in subs:

for… loops§



Often you’ll want to know not only the current value in a list, but also its location. For instance, if we run some trials like this. e.g.

oris = [0,45,90,180]
resps = [1, 1, 0, 1]
RTs = [0.324, 0.454, 0.432, 0.123]

How do we print the resp and RT that is associated with the equivalent ori ?



We could go back to old-style and loop through a set of indices to fetch the values:

for ii in range(len(resps)):
    thisOri = oris[ii]
    thisResp = resps[ii]
    thisRT = RTs[ii]
    print(ii, thisOri, thisResp, thisRT)


The need to know the current value AND its index in the list is so common that Python has a special function for it called enumerate:

for ii, thisOri in enumerate(oris):
    thisResp = resps[ii]
    thisRT = RTs[ii]
    print(ii, thisOri, thisResp, thisRT)

Common Use-cases§

Let’s use a loop to create a list of dictionaries:

for thisInt in range(5):
    thisEntry = {}
print('myList is now', myList)

print('printing one entry per line:')
for thisEntry in myList:

Common Use-cases§

If you use a dictionary in a loop it will return each of the keys:

man = {'name':'Jon', 'style':'geek', 'age':21}
for thisKey in man:
    print(f"This man's {thisKey} is {man[thisKey]}")

Dictionaries also have an items method, which returns a list of key/value pairs. We could iterate over the list of pairs, which means we could do this:

for thisKey, thisVal in man.items():
    print(f"This man's {thisKey} is {thisVal}")

Nesting loops§

You can nest one loop inside another (as deeply as you like). The inner loop will perform a full cycle on each pass through the outer loop:

for thisNum in range(5):
    for thisChar in 'abc':
        print(thisNum, thisChar)

Exercise: Switch the order of the two loops and try it again.


Nesting loops§

Remember indentation is key in deciding which of the loops a code line belongs to:

for thisNum in range(5):
    print(f'------------starting run {thisNum}')
    for thisChar in 'abc':
        print(thisNum, thisChar)
    print(f'------------finished run {thisNum}')

NB: Be careful with looping - too many levels of nesting can be a sign of code that is too complicated. In that case, Functions to the rescue!


while… loops§

If you want your loop to end based on some condition, rather than based on a certain number or iterations, then you could use a while…loop. For instance, an experiment might be based something on time rather than on repeats:

import time #time module is built into Python
t0=time.time() #time in secs
nReps = 0
while (time.time()-t0) < 0.5: #continue this loop for 0.5s
    nReps = nReps+1 # (try using the shorthand n+=1 in the shell)
print(f"we did {nReps:.0f} loops in 0.5s")

while… loops§

Or you might want to end the loop only when a valid response has occurred.:

from numpy import random
validKeys = 'az'
availableKeys = 'azqwertyuiop'
resp=None #None is a special value in Python for, well, none!
while resp==None:
    ii = random.randint(0,len(availableKeys))
    keyPress = availableKeys[ii]
    if keyPress in validKeys:
        print('At last')
        print(f"'{keyPress}' was not a valid key")
print(f"subject responded with '{resp}'")

Other than that, while...loops are really similar to for...loops (personally I use them less).


break and continue§

Sometimes you need to end a loop, or this repeat of a loop, prematurely.

They both only operate on the current loop - be careful if your loops are nested.


break and continue§

Let’s combine some of the earlier code. We’ll run trials as in the Enumerate demo and collect keypresses a bit like the while… loops section. But instead of waiting for a valid response, we’ll just ignore trials where subjects responded got the wrong keys. And if they hit ‘q’ we’ll abort the experiment:


break and continue§

Here are our imaginary experiment variables:

from numpy import random #we need a new lib for this demo
validKeys = 'az'
availableKeys = 'azqwertyuiop'
oris = [0,45,90,180]
resps = []
RTs = []

break and continue§

And this is how the simulated experiment might run:

for thisRep in range(5):#repeat 5 times
    random.shuffle(oris) #NB this shuffles the list 'in-place'
    for thisOri in oris:
        #Simulate a response
        ii = random.randint(0,len(availableKeys))
        keyPress = availableKeys[ii]
        RT = random.rand() #some number between 0-1
        #perform analysis
        if keyPress == 'q':
            print('experiment aborted')
        elif keyPress not in validKeys:
            print('invalid response')
            continue # to next trial (don't analyse further)
        #we got a useful trial so store info
        resps.append(keyPress) #the response from this trial
        trials.append(thisOri) #also store what this trial was

Loop Timing§

A for loop can take a variable amount of time to execute depending on how many items are being iterated and how quickly each iteration completes.

A while loop can be held for a certain amount of time, especially when used with Clock and CountdownTimer