4. Learning Python in a Network Context – Network Programmability and Automation [Book]

Understanding the for Loop

for loops in Python are awesome because when you use them you are usually looping, or iterating, over a set of objects, like those found in a list, string, or dictionary. for loops in other programming languages require an index and increment value to always be specified, which is not the case in Python.

Let’s start by reviewing what is sometimes called a for-in or for-each loop, which is the more common type of for loop in Python.

As in the previous sections, we start by reviewing a few basic examples.

The first is to print each object within a list. You can see in the following example that the syntax is simple, and again, much like what we learned when using conditionals and the while loop. The first statement or beginning of the for loop needs to end with a colon (:) and the code to be executed must be indented.

>>> vendors
['arista', 'juniper', 'big_switch', 'cisco']
>>>
>>> for vendor in vendors:
...     print('VENDOR: ' + vendor)
...
VENDOR:  arista
VENDOR:  juniper
VENDOR:  big_switch
VENDOR:  cisco
>>>

As mentioned earlier, this type of for loop is often called a for-in or for-each loop because you are iterating over each element in a given object.

In the example, the name of the object vendor is totally arbitrary and up to the user to define, and for each iteration, vendor is equal to that specific element. For example, in this example vendor equals arista during the first iteration, juniper in the second iteration, and so on.

To show that vendor can be named anything, let’s rename it to be network_vendor.

>>> for network_vendor in vendors:
...     print('VENDOR: ' + network_vendor)
...
VENDOR:  arista
VENDOR:  juniper
VENDOR:  big_switch
VENDOR:  cisco
>>>

Let’s now combine a few of the things learned so far with containment, conditionals, and loops.

The next example defines a new list of vendors. One of them is a great company, but just not cut out to be a network vendor! Then it defines approved_vendors, which is basically the proper, or approved, vendors for a given customer. This example loops through the vendors to ensure they are all approved, and if not, prints a statement saying so to the terminal.

>>> vendors = ['arista', 'juniper', 'big_switch', 'cisco', 'oreilly']
>>>
>>> approved_vendors = ['arista', 'juniper', 'big_switch', 'cisco']
>>>
>>> for vendor in vendors:
...     if vendor not in approved_vendors:
...         print('NETWORK VENDOR NOT APPROVED: ' + vendor)
...
NETWORK VENDOR NOT APPROVED:  oreilly
>>>

You can see that not can be used in conjunction with in, making it very powerful and easy to read what is happening.

We’ll now look at a more challenging example where we loop through a dictionary, while extracting data from another dictionary, and even get to use some built-in methods you learned earlier in this chapter.

To prepare for the next example, let’s build a dictionary that stores CLI commands to configure certain features on a network device:

>>> COMMANDS = {
...     'description': 'description {}',
...     'speed': 'speed {}',
...     'duplex': 'duplex {}',
... }
>>>
>>> print(COMMANDS)
{'duplex': 'duplex {}', 'speed': 'speed {}', 'description': 'description {}'}
>>>

We see that we have a dictionary that has three items (key-value pairs). Each item’s key is a network feature to configure, and each item’s value is the start of a command string that’ll configure that respective feature. These features include speed, duplex, and description. The values of the dictionary each have curly braces ({}) because we’ll be using the format() method of strings to insert variables.

Now that the COMMANDS dictionary is created, let’s create a second dictionary called CONFIG_PARAMS that will be used to dictate which commands will be executed and which value will be used for each command string defined in COMMANDS.

>>> CONFIG_PARAMS = {
...     'description': 'auto description by Python',
...     'speed': '10000',
...     'duplex': 'auto'
... }
>>>

We will now use a for loop to iterate through CONFIG_PARAMS() using the items built-in method for dictionaries. As we iterate through, we’ll use the key from CONFIG_PARAMS and use that to get the proper value, or command string, from COMMANDS. This is possible because they were prebuilt using the same key structure. The command string is returned with curly braces, but as soon as it’s returned, we use the format() method to insert the proper value, which happens to be the value in CONFIG_PARAMS.

Let’s a take a look.

>>> commands_list = []
>>>
>>> for feature, value in CONFIG_PARAMS.items():
...     command = COMMANDS.get(feature).format(value)
...     commands_list.append(command)
...
>>> commands_list.insert(0, 'interface Eth1/1')
>>>
>>> print(commands_list)
['interface Eth1/1', 'duplex auto', 'speed 10000',
  'description auto description by Python']
>>>

Now we’ll walk through this in even more detail. Please take your time and even test this out yourself while on the Python interactive interpreter.

In the first line commands_list is creating an empty list []. This is required in order to append() to this list later on.

We then use the items() built-in method as we loop through CONFIG_PARAMS. This was covered very briefly earlier in the chapter, but items() is giving you, the network developer, access to both the key and value of a given key-value pair at the same time. This example iterates over three key-value pairs, namely description/auto description by Python, speed/10000, and duplex/auto.

During each iteration—that is, for each key-value pair that is being referred to as the variables feature and value—a command is being pulled from the COMMANDS dictionary. If you recall, the get() method is used to get the value of a key-value pair when you specify the key. In the example, this key is the feature object. The value being returned is description {} for description, speed {} for speed, and duplex {} for duplex. As you can see, all of these objects being returned are strings, so then we are able to use the format() method to insert the value from CONFIG_PARAMS because we also saw earlier that multiple methods can be used together on the same line!

Once the value is inserted, the command is appended to commands_list. Once the commands are built, we insert() Eth1/1. This could have also been done first.

If you understand this example, you are at a really good point already with getting a grasp on Python!

You’ve now seen some of the most common types of for loops that allow you to iterate over lists and dictionaries. We’ll now take a look at another way to construct and use a for loop.

Using the enumerate() function

Occasionally, you may need to keep track of an index value as you loop through an object. We show this fairly briefly, since most examples that are reviewed are like the previous examples already covered.

enumerate() is used to enumerate the list and give an index value, and is often handy to determine the exact position of a given element.

The next example shows how to use enumerate() within a for loop. You’ll notice that the beginning part of the for loop looks like the dictionary examples, only unlike items(), which returns a key and value, enumerate() returns an index, starting at 0, and the object from the list that you are enumerating.

The example prints both the index and value to help you understand what it is doing:

>>> vendors = ['arista', 'juniper', 'big_switch', 'cisco']
>>>
>>> for index, each in enumerate(vendors):
...     print(index + ' ' + each)
...
0 arista
1 juniper
2 big_switch
3 cisco
>>>

Maybe you don’t need to print all of indices and values out. Maybe you only need the index for a given vendor. This is shown in the next example.

>>> for index, each in enumerate(vendors):
...     if each == 'arista':
...         print('arista index is: ' + index)
...
arista index is:  0
>>>

We’ve covered quite a bit of Python so far, from data types to conditionals to loops. However, we still haven’t covered how to efficiently reuse code through the use of functions. This is what we cover next.