MicroPython 'for' Loops'
Contents
Introduction
MicroPython is a “slim” version of Python specifically designed with a small footprint to efficiently run on memory constrained microcontrollers.
Computer languages provide a variety of statement types. Programmers would expect as a minium to find:
- Assignment statements
- Decision making statements
- Looping Statements
- while
- for
This article explores MicroPython's for looping statement.
The for Loop
A for loop is a type of loop that runs for a preset number of times. It also has the ability to iterate over the items of any sequence, such as a list or a string. The continue and break control statements are also available for use in a for loop. All up, this makes the Python for construct very powerful.
Syntaxfor i in <collection>: <loop body>
The for Loop with Numbers
The easiest way to use a for with numbers is with the MicroPython range() function.
Python range() function
The range() function returns a sequence of numbers, starting from 0 by default, and increments by 1 (by default), and stops immediately before a specified number.
The full syntax is:
range([start,] stop[, step])
start | Optional. An integer number specifying at which position to start. Default is 0 |
stop | Required. An integer number specifying at which position to stop immediately before. |
step | Optional. An integer number specifying the incrementation. Default is 1 |
When discussing while loops in the previous tutorial in this series, an example was given that summed the first 10,000 integers.
A much more machine efficient version can be written with a for loop.
Example 1
sum = 0 #initialise
for counter in range(1, 10001): sum += counter
print('Sum of 1..10,000 is', sum)
Output:
Sum of 1..10,000 is 50005000
It is an easy exercise to demonstrate the improvement gained with the use of a for loop in preference to a while loop when the number of loop iterations is known in advance.
Example 2
# Demonstrate that 'for' loops are more
# efficient than 'while' loops if the
# number of loop iterations are known
# in advance.
# Program sums first 1,000,000 integers using
# a 'for' loop then a 'while' loop.
# Both loops are timed.
# MicroPython library for timing functions.
import time
# Functions to use micro:bit hardware.
import microbit
# Time the 'for' loop
sum1 = 0
start = time.ticks_ms() # Get current time
for counter in range(1, 1000001): sum1 += counter
elapsed1 = time.ticks_ms() - start
# Time the 'while' loop
counter = 1
sum2 = 0
start = time.ticks_ms() # Get current time
while (counter <= 1000000):
sum2 += counter
counter += 1
elapsed2 = time.ticks_ms() - start
#Print the comparison
print("Loop using 'for'")
print('Sum of 1..1,000,000 is', sum1)
print('Time taken is', elapsed1/1000, 'seconds')
print()
print("Loop using 'while'")
print('Sum of 1..1,000,000 is', sum2)
print('Time taken is', elapsed2/1000, 'seconds')
print()
eff = (elapsed2 - elapsed1)/elapsed2 * 100
print("Improvement using 'for' loop is", eff, "%")
# Turn on LED display on micro:bit
# Signifies program has completed.
microbit.display.show(microbit.Image.SMILE)
This program was run on a micro:bit. It takes the time for the summation of the first 1,000,000 integers firstly using a for loop then repeats the exercise using a while loop.
The percentage efficiency gain is calculated and was found to be around 8%. This could be a significant improvement, for example, on a microprocessor running a critical real-time process. Output is shown below.
Output:Loop using 'for' Sum of 1..1,000,000 is 500000500000 Time taken is 45.421 seconds Loop using 'while' Sum of 1..1,000,000 is 500000500000 Time taken is 49.394 seconds Improvement using 'for' loop is 8.043488 %
The for Loop with Iterable Data Types
It is an easy task to loop through any iterable data type, retrieving each element, one after the other. MicroPython iterable data types include list, set, dictionary, string and tuple
Syntaxfor i in <collection>: <loop body> Each time through the loop, the variable i takes on the value of the next object in collection.Example 3
# Demonstrates how a 'for' loop can access all
# elements of an iterable data type in sequence.
# Program prints each element of various
# iterable data types - list, set,
# dictionary, string and tuple.
def PrintElements(var, var_type):
print(var_type, 'example... ', end="")
for element in var:
# Print all elements of the iterable
# on a single line.
print(element, end=' ')
print()
# list example
list1 = ['AB', 'CD', 'EF', 'GH', 'AB']
PrintElements(list1, 'list')
# set example
set1 = set(list1)
PrintElements(set1, 'set')
# dictionary example
dict1 = {'first':1, 'second':2, 'third':3}
PrintElements(dict1, 'dictionary')
# string example
string1 = 'Hello World'
PrintElements(string1, 'string')
# tuple example
tuple1 = tuple(list1)
PrintElements(tuple1, 'tuple')
This code tests the common Python iterable data types as mentioned above.
For code brevity a user defined function is used to implement the for loop. User defined functions are covered here. Note how the single function is able to handle all of these data types.
The program was executed on a micro:bit from the Mu Editor.
Output:list example... AB CD EF GH AB set example... EF GH AB CD dictionary example... second first third string example... H e l l o W o r l d tuple example... AB CD EF GH AB
List Comprehension
List comprehension is a useful shortcut technique to create a new list based from the data of an existing list.
Syntaxnewlist = [<expression> for <loop variable> in <list> (if condition)]
Here, expression can be a piece of code , for example a method, that returns a value. The elements of list will be appended to the newlist array if loop variable fulfills the condition.
Try the following on a micro:bit.
Example 4
# List comprehension example
# A new list ('newlist') is populated
# with all values that are even from
# a list of integers ('biglist').
# All negative values that meet the
# condition (even number) are converted to
# positive values before inserting into newlist.
biglist = [12, -57, 23, 45, -2, 98, -26]
newlist = [abs(i) for i in biglist if (i % 2 == 0)]
print('The new list is', newlist)
Output:
The new list is [12, 2, 98, 26]
Each value from the list of integers, biglist, is examined in turn. If the integer is an odd number then it is rejected. If it is an even number then it is converted to a positve value (if negative) and appended to newlist. This is all done in a single line of code!
The for-else Loop
In exactly the same manner as the while statement, an else may be appended to a for statement. The else code block will be run once the main loop has completed.
The control statements continue and break also apply with the for loop, exactly as they do for a while loop
A break statement will cause an else block (if present) to be skipped.
Syntaxfor i in <collection>: <loop body> else: <code block> The else block will run after the for loop completes.Example 5
# Demonstrates for.. else.. loop.
# Inverts the case of all letters in a string
# Ignores characters from a given set ('ignore')
string1 = 'I Saw: - ##*A Striped Zebra*##'
# Characters that are to be ignored
ignore = {':', '*', '#'}
print('Original string: ', string1)
print('Converted string: ', end='')
for ch in string1:
if (ch in ignore):
# Ignore this character
continue
if ch.isupper():
# Uppercase, convert to lowercase
print(ch.lower(), end='')
elif ch.islower():
# lowercase, convert to uppercase
print(ch.upper(), end='')
else:
# Not a letter, no conversion
print(ch, end='')
else:
print('\nProgram ending...')
Output:
Original str: I Saw: - ##*A Striped Zebra*## Converted str: i sAW - a sTRIPED zEBRA
Note the use of continue to skip the second conditional statement if a character that must be ignored is found. The else block is still executed when the for loop is completed.
The next program adds a piece of extra functionality to the last example. When the fifth occurrence of a lowercase letter in the string being parsed is found then the for loop is immediately terminated.
Example 6
# Demonstrates for.. else.. loop.
# (1) Inverts the case of all letters
# in a string.
# (2) Ignores characters from a given
# set ('ignore')
# (3) Count lowercase letters found.
# Stop the program on the fifth
# occurrence of lowercase letters.
string1 = 'I Saw: - ##*A Striped Zebra*##'
# Characters that are to be ignored
ignore = {':', '*', '#'}
# Lowercase letter count
lcount = 0
print('Original str:', string1)
print('Converted str:', end='')
for ch in string1:
if (ch in ignore):
# Ignore this character
continue
if ch.isupper():
# Uppercase, convert to lowercase
print(ch.lower(), end='')
elif ch.islower():
lcount += 1
# Stop program if this is the fifth
# occurence of a lowercase letter.
if (lcount == 5):
print()
break
# Convert lowercase to uppercase
print(ch.upper(), end='')
else:
# Not a letter, no conversion
print(ch, end='')
else:
print('\nProgram ending...')
Output:
Original str: I Saw: - ##*A Striped Zebra*## Converted str: i sAW - a sTR
The fifth occurrence of a lowercase letter occurs with the ‘i’ in the word ‘Striped’. Execution of the for loop immediately terminates. Note that this also prevents the else code from running.
Nested Loops
Both while and for loops can be nested within each other to any practical depth and in any combination. Care needs to be taken though that readability doesn't overly suffer.
The following program finds and prints the prime numbers between a given inclusive range (100..150). The program demonstrates the use of a nested for loop inside the main for loop.
Example 7
# Demonstrates a 'for' loop nested in
# another 'for' loop.
# Returns all prime numbers inclusive between
# two given values ('low' and 'high').
low = 100
high = 150
print('Prime numbers between',
low, 'and', high, 'are:')
for num in range(low, high + 1):
# Check if 'num' can be divided by
# anything other than 1 or itself
# while leaving no remainder.
for divisor in range(2, num//2 + 1):
if (num % divisor == 0):
# 'num' is not a prime
break
else:
# 'num' is a prime
print(num, end=' ')
print()
Output:
Prime numbers between 100 and 150 are: 101 103 107 109 113 127 131 137 139 149
Each number in the range is taken one by one and checked whether it can be divided evenly by any number other than one (1) or itself. This work is done in the nested for loop.
The next program also returns prime numbers. This time the first ‘n’ primes are returned. Specifically the program has n = 15
, but could be easily modified to accept a value input by a user.
# Demonstrates a 'for' loop nested
# in a 'while' loop.
# Program returns the first 'n' prime numbers.
# Number of prime numbers required.
n = 15
# Start point for prime number search
num = 2
# Number of prime number found
count = 0
print('The first', n, ' prime numbers are:')
while (count < n):
# Check if 'num' can be divided by
# anything other than 1 or itself
# while leaving no remainder.
for divisor in range(2, num//2 + 1):
if (num % divisor == 0):
# 'num' not a prime
break
else:
# 'num' is prime
print(num, end=' ')
count += 1
num += 1
print()
The first 15 prime numbers are: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
This time there is a nested for loop within the main while loop. The outer while loop continues till the required number of primes are found. The nested for loop incrementally tests numbers starting at two (2) for prime number membership. The algorithm is the same as used in Example 6.