I’m learning the basics of Python and these are my publically available notes for my reference. Hopefully they are useful for my readers as well.

The For loop in Python is used to iterate through different objects such as lists or dictionaries. The power of the For loop is that it can run as many times as needed and then stop without having to define the number of times it should run. It can also be used to run n number of times where we define n ourselves.

I’ll give some examples related to networking to make it more interesting. Let’s say that we want to create a lot of loopbacks so that we can advertise routes in BGP to play around with prefix-lists. We will create 10 loopbacks. This means that the For loop should run 10 times, we can use the range command for this. The iterator will start at 0 and have a stepping by 1 by default which means that our first loopback will be loopback0 and our first network will be 10.0.0.1/32.

for loopback in range(10):
    print "interface loopback{}".format(loopback)
    print "ip address 10.0.{}.1 255.255.255.255".format(loopback)

Before I show the output let’s walk this through line by line.

for loopback in range(10):

The for loop is iniated and I give the iterator the name of loopback. This could have been named iterator, i, n, number or anything that makes sense to you. The range(10) means that it will run 10 times and the iterator will have the value of 0 up to 9.

print "interface loopback{}".format(loopback)

This will print “interface loopback” and {} is used to reference my iterator “loopback” which is the integer 0 up to 9. I used the .format syntax to not insert a space between “loopback” and the number. My initial code was the one below which would have inserted a space:

print "interface loopback", loopback

The final line looks like this:

print "ip address 10.0.{}.1 255.255.255.255".format(loopback)

This prints “ip address 10.0..1 255.255.255.255″ and once again I’m referencing the iterator by using {} and .format.

This will generate the following output:

daniel@daniel-iperf5:~$ python loopback.py 
interface loopback0
ip address 10.0.0.1 255.255.255.255
interface loopback1
ip address 10.0.1.1 255.255.255.255
interface loopback2
ip address 10.0.2.1 255.255.255.255
interface loopback3
ip address 10.0.3.1 255.255.255.255
interface loopback4
ip address 10.0.4.1 255.255.255.255
interface loopback5
ip address 10.0.5.1 255.255.255.255
interface loopback6
ip address 10.0.6.1 255.255.255.255
interface loopback7
ip address 10.0.7.1 255.255.255.255
interface loopback8
ip address 10.0.8.1 255.255.255.255
interface loopback9
ip address 10.0.9.1 255.255.255.255

Quite handy if you want to generate a lot of interfaces. What if we wanted to run from 1 to 10 instead? We can change the range slightly. First let’s check the syntax of range.

daniel@daniel-iperf5:~$ python
Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> help(range)
Help on built-in function range in module __builtin__:

range(...)
    range(stop) -> list of integers
    range(start, stop[, step]) -> list of integers
    
    Return a list containing an arithmetic progression of integers.
    range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
    When step is given, it specifies the increment (or decrement).
    For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
    These are exactly the valid indices for a list of 4 elements.

If we just define one value, then that is our stop value. We can also define a start value. Let’s slightly update the script:

for loopback in range(1, 10):
    print "interface loopback{}".format(loopback)
    print "ip address 10.0.{}.1 255.255.255.255".format(loopback)

And then we run the script:

daniel@daniel-iperf5:~$ python loopback2.py 
interface loopback1
ip address 10.0.1.1 255.255.255.255
interface loopback2
ip address 10.0.2.1 255.255.255.255
interface loopback3
ip address 10.0.3.1 255.255.255.255
interface loopback4
ip address 10.0.4.1 255.255.255.255
interface loopback5
ip address 10.0.5.1 255.255.255.255
interface loopback6
ip address 10.0.6.1 255.255.255.255
interface loopback7
ip address 10.0.7.1 255.255.255.255
interface loopback8
ip address 10.0.8.1 255.255.255.255
interface loopback9
ip address 10.0.9.1 255.255.255.255

Not quite what we expected. The issue here is that when we define the stop value in the for loop this is not inclusive, this means the for loop stops at 9. If we want to stop at 10 we need to set 11 as the value.

for loopback in range(1, 11):
    print "interface loopback{}".format(loopback)
    print "ip address 10.0.{}.1 255.255.255.255".format(loopback)

Then we get the expected output:

daniel@daniel-iperf5:~$ python loopback2.py 
interface loopback1
ip address 10.0.1.1 255.255.255.255
interface loopback2
ip address 10.0.2.1 255.255.255.255
interface loopback3
ip address 10.0.3.1 255.255.255.255
interface loopback4
ip address 10.0.4.1 255.255.255.255
interface loopback5
ip address 10.0.5.1 255.255.255.255
interface loopback6
ip address 10.0.6.1 255.255.255.255
interface loopback7
ip address 10.0.7.1 255.255.255.255
interface loopback8
ip address 10.0.8.1 255.255.255.255
interface loopback9
ip address 10.0.9.1 255.255.255.255
interface loopback10
ip address 10.0.10.1 255.255.255.255

So far so good. What if we want to only have odd loopbacks and networks? We can change the stepping to two and I’ll set the stop value to 12 so that 11 should be the last one printed.

for loopback in range(1, 12, 2):
    print "interface loopback{}".format(loopback)
    print "ip address 10.0.{}.1 255.255.255.255".format(loopback)

The output then looks like this:

daniel@daniel-iperf5:~$ python loopback3.py 
interface loopback1
ip address 10.0.1.1 255.255.255.255
interface loopback3
ip address 10.0.3.1 255.255.255.255
interface loopback5
ip address 10.0.5.1 255.255.255.255
interface loopback7
ip address 10.0.7.1 255.255.255.255
interface loopback9
ip address 10.0.9.1 255.255.255.255
interface loopback11
ip address 10.0.11.1 255.255.255.255

That is the basic of using the For loop with the range command. It’s useful when we know how many times we want to run the loop. In Python 2.x it is also possible to use For loops with xrange() which is more efficient for large numbers. This is overkill for this post though and in Python 3.x, range() does what xrange() did in Python 2.x so it’s better to get used to using the range() syntax.

So far we have used a For loop with a range to loop a number of times. What if we don’t know how many times we want to loop? Such as looping through a list. Let’s create a list consisting of interface names. These are our trunk ports.

trunks = ["Eth1/1", "Eth1/4", "Eth1/12"]

We could still use a For loop with range since we know the number of strings in the list. What if it’s a long list though? Maybe we don’t want to count the number of entries manually. We can use the builtin len() function to check the length of the list. The For loop is then run for the length of the list.

trunks = ["Eth1/1", "Eth1/4", "Eth1/12"]

for trunk in range(len(trunks)):
    print trunks[trunk]

The range here is set to the length of the list, which is three. Remember though that the three is non inclusive. We then access slices from the list in the print statement. The first time the loop runs the print statement is set to trunks[0] which corresponds to Eth1/1. This is what it looks like if we run it:

daniel@daniel-iperf5:~$ python trunks.py 
Eth1/1
Eth1/4
Eth1/12

There is a simpler way to loop through lists though. We can build the loop like this instead:

trunks = ["Eth1/1", "Eth1/4", "Eth1/12"]

for trunk in trunks:            
    print trunk

Python is powerful in that it can loop through almost anything such as lists, dictionaries etc. and the loop will run for the length of the object.

daniel@daniel-iperf5:~$ python trunks2.py 
Eth1/1
Eth1/4
Eth1/12

What if we want to add an index to our list? We can use the builtin function enumerate() for that.

trunks = ["Eth1/1", "Eth1/4", "Eth1/12"]

for index, value in enumerate(trunks):
    print index, value

The output then looks like this:

daniel@daniel-iperf5:~$ python trunks3.py 
0 Eth1/1
1 Eth1/4
2 Eth1/12

The index always starts at 0 so if we want to start at 1 we can modify the loop slightly:

trunks = ["Eth1/1", "Eth1/4", "Eth1/12"]

for index, value in enumerate(trunks):
    print index+1, value

The index will then start at 1 since 0 + 1 = 1.

daniel@daniel-iperf5:~$ python trunks3.py 
1 Eth1/1
2 Eth1/4
3 Eth1/12

Is it possible to loop through two lists simultaneously? Absolutely. Maybe we have one list for our trunk ports and one list for descriptions. We can then leverage the builtin zip() function which “zips” the lists together and produces a stream of pairs.

trunks = ["Eth1/1", "Eth1/4", "Eth1/12"]
descriptions = ["To CoreSW-1", "To CoreSW-2", "To CoreSW-3"]

for trunk, description in zip(trunks, descriptions):
    print "interface {}".format(trunk)
    print "description {}".format(description)

The loop will run through both lists, print “interface” followed by the interface name and on a new line “description” followed by the port description.

daniel@daniel-iperf5:~$ python trunks4.py 
interface Eth1/1
description To CoreSW-1
interface Eth1/4
description To CoreSW-2
interface Eth1/12
description To CoreSW-3

If the lists are different length, zip() will truncate to the shortest list. Let’s try this by adding another interface to the trunks list.

trunks = ["Eth1/1", "Eth1/4", "Eth1/12", "Eth1/16"]
descriptions = ["To CoreSW-1", "To CoreSW-2", "To CoreSW-3"]

for trunk, description in zip(trunks, descriptions):
    print "interface {}".format(trunk)
    print "description {}".format(description)

As can be seen below the loop will then stop and not print anything for Eth1/16:

daniel@daniel-iperf5:~$ python trunks5.py 
interface Eth1/1
description To CoreSW-1
interface Eth1/4
description To CoreSW-2
interface Eth1/12
description To CoreSW-3

If we want to continue printing even if one list is shorter we can use the builtin map() function and replace the missing value with None:

trunks = ["Eth1/1", "Eth1/4", "Eth1/12", "Eth1/16"]
descriptions = ["To CoreSW-1", "To CoreSW-2", "To CoreSW-3"]

for trunk, description in map(None,trunks, descriptions):
    print "interface {}".format(trunk)
    print "description {}".format(description)

We replaced zip() with map() and told it to replace empty values with “None”. This is then the result of running the script:

daniel@daniel-iperf5:~$ python trunks6.py 
interface Eth1/1
description To CoreSW-1
interface Eth1/4
description To CoreSW-2
interface Eth1/12
description To CoreSW-3
interface Eth1/16
description None

All interfaces were now printed and since there was no description for Eth1/16 we replaced it with “None”.

What if we want to loop through a dictionary? We can do this the same way as a list:

access_ports = {"Eth1/1": "To Core router", "Eth1/4": "To Firewall", "Eth1/12": "To monitoring workstation"}

for key in access_ports:              
    print key

Here we have created a dictionary consisting of access ports and their description. When a dictionary is printed the order is not guaranteed and what gets printed is the key:

daniel@daniel-iperf5:~$ python access.py 
Eth1/4
Eth1/12
Eth1/1

It is possible to access the value of the key in the following way:

access_ports = {"Eth1/1": "To Core router", "Eth1/4": "To Firewall", "Eth1/12": "To monitoring workstation"}

for key in access_ports:
    value = access_ports[key]
    print key, value

We are accessing the value by entering a key which is different for each iteration. This produces the following:

daniel@daniel-iperf5:~$ python access2.py 
Eth1/4 To Firewall
Eth1/12 To monitoring workstation
Eth1/1 To Core router

It’s a bit cleaner to use the builtin function iteritems() though, and that looks like the following:

access_ports = {"Eth1/1": "To Core router", "Eth1/4": "To Firewall", "Eth1/12": "To monitoring workstation"}

for key, value in access_ports.iteritems():
    print key, value

Here we can access the value directly without referencing to the key first. This also produces the same output as previously:

daniel@daniel-iperf5:~$ python access3.py 
Eth1/4 To Firewall
Eth1/12 To monitoring workstation
Eth1/1 To Core router

This blog post is mostly for my own reference but I hope it’s helpful for others and shows how you can use For loops in combination with standard networking configuration.

Python – For Loops
Tagged on:             

7 thoughts on “Python – For Loops

  • January 29, 2017 at 7:02 pm
    Permalink

    you can also do this (w/o formatting)
    for loopback in range(0,11):
    print(“interface loopback%d” % loopback)
    print(“ip address 10.0.%d.255″% loopback)

    Reply
  • January 30, 2017 at 8:58 am
    Permalink

    Hi Daniel,

    Could you let us know, which book/Post/Blog or etc need to read if anyone wants to learn network
    programmability…looking forward to hearing from you asap

    Thanks

    Reply
    • February 12, 2017 at 11:30 am
      Permalink

      Hi Vikas,

      I recommend you start with Kirk Byers course. See my recent blog posts. There’s also an interesting course at Udemy about network programmability. I’m planning on taking it.

      Reply
  • January 30, 2017 at 3:05 pm
    Permalink

    Thanks for this post, Daniel. I’ve played with Python for a while but never use or heard of zip() and map() to loop through two lists simultaneously. Now that is definitely something I will add under my belt 🙂

    Reply
  • January 30, 2017 at 3:24 pm
    Permalink

    And to have a dictionary in a specific order we want, we can use the built-in function OrderedDict(). But we have to feed this function with a tuple of tuples:

    from collections import OrderedDict
    my_data = ((“a”, 1), (“b”, 2), (“c”, 3))
    my_dict = OrderedDict(my_data)
    for key, value in my_dict.items():
    print key, value

    Output:
    a 1
    b 2
    c 3

    Reply
    • January 31, 2017 at 8:06 am
      Permalink

      That’s good to know. Thanks! 🙂

      Reply
  • Pingback:Python - Kirk Byers Course Week 2 Part 3 - Daniels Networking Blog

Leave a Reply

Your email address will not be published. Required fields are marked *