Hello my friend,
We have discussed so far ordinary variables in string, numeric and Boolean formats and list variables. Today we’ll close the review of the variables by covering the last, and definitely not least, type of variables called Python dictionary. In fact, this one of the most useful for us from the network automation perspective.
Network automation training – boost your career
Don’t wait to be kicked out of IT business. Join our network automation training to secure your job in future. Come to NetDevOps side.
How does the training differ from this blog post series? Here you get the basics and learn some programming concepts in general, whereas in the training you get comprehensive set of knowledge with the detailed examples how to use Python for the network and IT automation. You need both.
What are we going to do today?
The Python dictionary is a structured data. At a glance it might be similar to the list from the syntax prospective. However, it has a significant difference: in the list each element has an index (numerical value starting from 0), whereas in the dictionary each element has a key (string value you define based on your logic). Today you will learn:
- How to create a Python dictionary
- How to add elements to the dictionary directly or using update() function
- How to remove the elements from the directory using pop() function or del command
- How to create a nested Python dictionary and mix it with list data types
Let’s crack on, my friend!
If you want to know how to install Python 3.8, check the corresponding blogpost.
Why does it matter?
As the network automation is all about working with data models, the dictionary is used internally as Python’s representation of the data model. The dictionaries existed in Python 2.7 and they exist in Python 3.8 as well. Therefore, if you have your data model structured in any external files, such as JSON or YAML, once you import it in the Python script, it will be represented as dictionary.
In future Code EXpress (CEX) blogposts you will learn how to convert JSON and YAML files into the Python 3.8 using modules
Usage of dictionaries allows you to group the variables per your need to ease working with them, as that creates some logical context of the variables
How are we doing that?
Following the approach, we have started earlier, we create a new directory in our GitHub Python’s lessons:
1
2
3
4 $ cd CEX
$ mkdir 06
$ cd 06
$
Inside the directory we create a new file called lists.py, which we will use during this class:
1
2
3 $ touch dict.py && chmod a+x dict.py
$ cat dict.py
#!/usr/local/bin/python3.8
If you have a question to chmod command or to the path to the Python interpreter, take a look on the corresponding class.
Now we can focus on the Python dictionary itself. In general, the dictionary might have multiple levels of the nesting creating the necessary hierarchy for your data structure. It also supports the possibility to have different data types including lists as its elements. The overall Syntax for the Python dictionary is dict = {‘key’: ‘value’}, where:
- dict is a name of the variable;
- {} is a framing defining the Python dictionary type;
- key is a name of the key (e.g. name of the element);
- value is a value the element has (it can be string, number, Boolean, list, other dictionary);
To give you understanding how to work with the Python dictionary, let’s start with the basic example.
#1. Simple Python dictionary
In our file we create a Python dictionary containing two keys:
1
2
3
4
5
6
7
8
9 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
As you see, you can document the variable in a single string or in a multiple, it doesn’t matter for a Python 3.8. What matters, and this is a requirement, is that the sibling elements have a comma separator between them. In the provided snippet, the key ‘address’ has a string value, whereas element ‘prefix’ is a number.
Let’s execute the script to get the whole dictionary:
1
2 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
In order to call a specific element, you need to call it using dict[‘key’] syntax:
1
2
3
4
5
6
7
8
9
10 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
print('The device IP is {}'.format(ip['address']))
And the result of the script execution:
1
2
3 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
The device IP is 192.168.100.1
If you need to add the new element to the created dictionary, you can use very simple construction:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
print('The device IP is {}'.format(ip['address']))
ip['family'] = 'ipv4'
print('Your dictionary is {}'.format(ip))
Point out, that such an approach works only, if the dictionary exists. If you try to create an element and the dictionary simultaneously using this command, the process will fail.
As you can see from the demonstration below, the new key was successful added to the Python dictionary:
1
2
3
4 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
The device IP is 192.168.100.1
Your dictionary is {'address': '192.168.100.1', 'prefix': 24, 'family': 'ipv4'}
You might spot that the same way of adding the elements exists in a Python’s list.
In the same way, if you need to delete the element you can use the del command:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
print('The device IP is {}'.format(ip['address']))
ip['family'] = 'ipv4'
print('Your dictionary is {}'.format(ip))
del ip['prefix']
print('Your dictionary is {}'.format(ip))
The outcome of this del instruction is the following:
1
2
3
4
5 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
The device IP is 192.168.100.1
Your dictionary is {'address': '192.168.100.1', 'prefix': 24, 'family': 'ipv4'}
Your dictionary is {'address': '192.168.100.1', 'family': 'ipv4'}
Another important note for you, if the key doesn’t exist, the deletion will fail:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
print('The device IP is {}'.format(ip['address']))
ip['family'] = 'ipv4'
print('Your dictionary is {}'.format(ip))
del ip['prefix']
print('Your dictionary is {}'.format(ip))
del ip['prefix']
print('Your dictionary is {}'.format(ip))
And the outcome:
1
2
3
4
5
6
7
8 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
The device IP is 192.168.100.1
Your dictionary is {'address': '192.168.100.1', 'prefix': 24, 'family': 'ipv4'}
Your dictionary is {'address': '192.168.100.1', 'family': 'ipv4'}
Traceback (most recent call last):
File "./dict.py", line 19, in <module>
del ip['prefix']
Therefore, be careful once you are working with the del command.
#2. Functions update() and pop() to work with the Python dictionary
Much in the same way like the Python list elements could be managed with functions, the Python dictionary can be managed with some functions as well.
You can add the element (or modify the value of the existing element) using the update() function applied to the dictionary:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
print('The device IP is {}'.format(ip['address']))
ip['family'] = 'ipv4'
print('Your dictionary is {}'.format(ip))
del ip['prefix']
print('Your dictionary is {}'.format(ip))
ip.update({'type': 'primary'})
print('Your dictionary is {}'.format(ip))
The input to the update() function is a key/value pair, which you want to add or modify inside the Python’s dictionary. The outcome of this Python’s script execution will be:
1
2
3
4
5
6 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
The device IP is 192.168.100.1
Your dictionary is {'address': '192.168.100.1', 'prefix': 24, 'family': 'ipv4'}
Your dictionary is {'address': '192.168.100.1', 'family': 'ipv4'}
Your dictionary is {'address': '192.168.100.1', 'family': 'ipv4', 'type': 'primary'}
The opposite operation is pop(). Exactly like in the list, it returns the value of the variable it deletes. However, here is a nuance: you can provide a value, which will be returned if the requested key doesn’t exist. This will prevent the situation of the script errors like was explained above. Let’s craft such an operation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 $ cat dict.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
print('Your dictionary is {}'.format(ip))
print('The device IP is {}'.format(ip['address']))
ip['family'] = 'ipv4'
print('Your dictionary is {}'.format(ip))
del ip['prefix']
print('Your dictionary is {}'.format(ip))
ip.update({'type': 'primary'})
print('Your dictionary is {}'.format(ip))
address = ip.pop('address', None)
print('Your dictionary is {} and removed element is {}'.format(ip, address))
address = ip.pop('address', None)
print('Your dictionary is {} and removed element is {}'.format(ip, address))
As you see, the function pop() has two arguments: the first one is the name of the element to be deleted and the second is the default value to be returned in case the element doesn’t exist. If you execute this script now, you will observe the following results:
1
2
3
4
5
6
7
8 $ ./dict.py
Your dictionary is {'address': '192.168.100.1', 'prefix': 24}
The device IP is 192.168.100.1
Your dictionary is {'address': '192.168.100.1', 'prefix': 24, 'family': 'ipv4'}
Your dictionary is {'address': '192.168.100.1', 'family': 'ipv4'}
Your dictionary is {'address': '192.168.100.1', 'family': 'ipv4', 'type': 'primary'}
Your dictionary is {'family': 'ipv4', 'type': 'primary'} and removed element is 192.168.100.1
Your dictionary is {'family': 'ipv4', 'type': 'primary'} and removed element is None
Even in case there is no key exists, you don’t get an error, just None value instead.
#3. Nested Python dictionary and mixing Python dictionary and Python list
Having learned the basics of the Python’s dictionary syntax and its functions such del, update() and pop(), you can now easily work with high scale Python’s dictionaries. There are no limits in terms of the nesting or mixing various elements inside a single dictionary as long as you follow the syntax rules of the Python’s dictionary, Python’s list, other variables and so on. In a nutshell, you can have:
- Any variable’s type as part of dictionary
- Another dictionary as a value of a certain key (nested dictionary)
- A list as a value of a certain key (nested list)
- A list consisting of dictionary elements with any level of nesting
- Any mix of the mentioned elements
And in in the real scenarios (e.g. network automation) you will work with such mixes. Take a look on the following Python’s dictionary:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 $ cat openconfig_dict.py
#!/usr/local/bin/python3.8
openconfig_interfaces = {
"interfaces": {
"interface": [
{
"name": "Ethernet1",
"config": {
"name": "Ethernet1",
"type": "ethernetCsmacd",
"mtu": 1514,
"enabled": True
}
},
{
"name": "Ethernet2",
"config": {
"name": "Ethernet2",
"type": "ethernetCsmacd",
"mtu": 1514,
"enabled": True
}
}
]
}
}
This snippet contains the real representation of the OpenConfig YANG module translated into the Python dictionary. When you analyse it carefully, you will find the following relations between the elements:
- the list interface is a value of the interfaces key;
- the list interface consists of the elements, which is dictionary itself;
- each element of the list interfaces has another level of nesting, config is a container.
To navigate across this data structure, you need to combine your knowledge of the Python’s list and dictionary:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 $ cat openconfig_dict.py
#!/usr/local/bin/python3.8
openconfig_interfaces = {
"interfaces": {
"interface": [
{
"name": "Ethernet1",
"config": {
"name": "Ethernet1",
"type": "ethernetCsmacd",
"mtu": 1514,
"enabled": True
}
},
{
"name": "Ethernet2",
"config": {
"name": "Ethernet2",
"type": "ethernetCsmacd",
"mtu": 1514,
"enabled": True
}
}
]
}
}
print('The whole data model is: {}'.format(openconfig_interfaces))
print('You have the following interfaces: {}'.format(openconfig_interfaces['interfaces']['interface']))
print('One of the interfaces has the following name: {}'.format(openconfig_interfaces['interfaces']['interface'][0]['config']['name']))
The execution of this Python’s code gives you the following results:
1
2
3
4 $ ./openconfig_dict.py
The whole data model is: {'interfaces': {'interface': [{'name': 'Ethernet1', 'config': {'name': 'Ethernet1', 'type': 'ethernetCsmacd', 'mtu': 1514, 'enabled': True}}, {'name': 'Ethernet2', 'config': {'name': 'Ethernet2', 'type': 'ethernetCsmacd', 'mtu': 1514, 'enabled': True}}]}}
You have the following interfaces: [{'name': 'Ethernet1', 'config': {'name': 'Ethernet1', 'type': 'ethernetCsmacd', 'mtu': 1514, 'enabled': True}}, {'name': 'Ethernet2', 'config': {'name': 'Ethernet2', 'type': 'ethernetCsmacd', 'mtu': 1514, 'enabled': True}}]
One of the interfaces has the following name: Ethernet1
#4. Merging two Python dictionaries
Before we call the current article, let’s touch the last point. In the discussion about Python’s lists we have provided you a way how to add one list to another using extend() function. In the current article, we’ve covered the addition of the elements to the Python’s dictionary using update() function. There is one alternative to the latter function, in case you want to create a new dictionary merging the two existing one. The following example shows how you can do that using the syntax new_dict = {**existing_dict_1, **existing_dict_2}:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 $ cat merge.py
#!/usr/local/bin/python3.8
ip = {
'address': '192.168.100.1',
'prefix': 24
}
phy = {
'name': 'Ethernet1',
'speed': 10000,
'mtu': 1514,
'enabled': True,
'description': 'Test Interface'
}
interface = {**ip, **phy}
print('The interface data is: {}'.format(interface))
Using this approach, you can merge any amount of the dictionaries you need.
Let’s take a look how this Python’s code work:
1
2 $ ./merge.py
The interface data is: {'address': '192.168.100.1', 'prefix': 24, 'name': 'Ethernet1', 'speed': 10000, 'mtu': 1514, 'enabled': True, 'description': 'Test Interface'}
The new Python’s dictionary is successfully created out of two existing. That’s it for today, mate.
If you prefer video
If you prefer watching the video instead of reading blogpost, that is also wonderful. We value your time and want to create for you the best experience possible. Therefore, for all the blogposts associated with CEX series, we will record the video and post it on our YouTube channel.
And here is the video for this blogpost:
What else shall you try?
Programming is all around testing, trying, breaking and fixing the things. Try to do the following things to collect some more experience:
- Create empty list and using update() function add some elements to it.
- Add using update() also a list element to the dictionary
- Using the append() function add the dictionary to that list
Lessons in GitHub
You can find the final working versions of the files from this blog at our GitHub page.
Conclusion
So far, my friend, you should know all the major data types and structures of the Python, if you followed all our series. Working with the data models or data structures is a corner stone of the network automation and network programmability. Take some time to test various concepts and examples before moving further. Take care and goodbye!
Support us
P.S.
If you have further questions or you need help with your networks, I’m happy to assist you, just send me message. Also don’t forget to share the article on your social media, if you like it.
BR,
Anton Karneliuk
Pretty beneficial, looking forwards to coming back.