Hello my friend,
In this post we will start exploring how to store multiple values in a single variable. There are multiple approaches how this can be achieved both in Python and Go (Golang). The first one we’ll cover is using ordered something of elements. We’ve used word “something”, because in Python this is called list, whilst Go (Golang) use the term slice. Let’s see what is similar and what is not between Python and Go (Golang).
Who Else Deals With Network Automation?
Just this week there was a massive event Autocon2, which is the biggest gathering of network automation enthusiasts from the entire world. People from different industries shared their experience, success stories and challenges. Whilst many of those insights are different, all of them revolve around the central idea that network automation is a must.
We have started doing network automation trainings before it become mainstream, yet we constantly update its content with new things. Start your training today to onboard the training, which is just leaving the platform.:
We offer the following training programs in network automation for you:
- Zero-to-Hero Network Automation Training
- High-scale automation with Nornir
- Ansible Automation Orchestration with Ansble Tower / AWX
- Expert-level training: Closed-loop Automation and Next-generation Monitoring
During these trainings you will learn the following topics:
- Success and failure strategies to build the automation tools.
- Principles of software developments and the most useful and convenient tools.
- Data encoding (free-text, XML, JSON, YAML, Protobuf).
- Model-driven network automation with YANG, NETCONF, RESTCONF, GNMI.
- Full configuration templating with Jinja2 based on the source of truth (NetBox).
- Best programming languages (Python, Bash) for developing automation
- The most rock-solid and functional tools for configuration management (Ansible) and Python-based automation frameworks (Nornir).
- Network automation infrastructure (Linux, Linux networking, KVM, Docker).
- Orchestration of automation workflows with AWX and its integration with NetBox, GitHub, as well as custom execution environments for better scalability.
- Collection network data via SNMP and streaming telemetry with Prometheus
- Building API gateways with Python leveraging Fast API
- Integration of alerting with Slack and your own APIs
- … and many more
Moreover, we put all mentions technologies in the context of real use cases, which our team has solved and are solving in various projects in the service providers, enterprise and data centre networks and systems across the Europe and USA. That gives you opportunity to ask questions to understand the solutions in-depth and have discussions about your own projects. And on top of that, each technology is provided with online demos and labs to master your skills thoroughly. Such a mixture creates a unique learning environment, which all students value so much. Join us and unleash your potential.
What Are We Going To Talk Today About?
In order for you to be able to write any meaningful application, it is critical to be able to handle multiple values within a single variable. To give you some ideas from network and IT infrastructure automation:
- it could be a sequence of CLI commands you are to send to your Cisco Nexus switch or Nokia SR 7750 router.
- it could also be a sequence of text segments which, you receive back from network devices in response to your commands.
- It could be credentials expressed in a sequence with one element being password and another username.
And this list continues going on and on. Right, I have used word “list”, which is actually a term used for sequence of items in Python. So what we are going to discuss today is provided in another list:
- What are the the lists, arrays, slices in Python and Go (Golang)?
- How to create and to use them?
- What the most popular functions to deal with lists/slices and how to use them?
Explanation
In general, irrespective to programming languages, list is a sequence of elements. Even in this blog post I’ve already used a numbered list, where elements are prepended with a digit (1, 2, 3,..), and an unnumbered list, where elements starts with dash “-“. When we talk about programming languages though, the terminology matters. Let’s review it:
- Array as a term exists in multiple programming languages (e.g., C/C++, Java, Go/Golang), but not in all (it doesn’t exist in Python as such). In a nutshell, array is a fixed-length sequence of elements of the same data type. As it comes from the description, at the moment you create the array you need to define how many elements it will have, and it cannot be more than that. This behavior roots to how memory is allocated and is managed in computers.
- List as a term exists in some programming languages (e.g., Python) and with some modifications in other programming languages, such as linked list or double linked list in C/C++. As we talk about Python, I will not focus on C/C++ specifics here. In Python list has unlimited length (ultimately it is rather limited to the size of your RAM) and it doesn’t enforce the data type of elements, which means that you can store integers, strings, boolean, floats or even other lists inside the elements of the same list.
- Slices as a term exists in multiple programming languages with different meanings: in Go (Golang) it means the array with the unlimited length, whereas in Python it means the subset of a list defined by starting and ending index. Without going too much in details of memory management, it is suffice to say that under the hood slice creates array and deals with changing its length on your behalf.
- Index is applicable to all aforementioned structures: arrays, lists, slices and it is a number representing the sequenced order element starting from the beginning of the structure (the first element of index/slice/list has index 0, the second element has index 1, etc).
As a key take a way:
- In Python you always deal with list, you can put there any amount of any data types.
- In Go (Golang) you deal with arrays or slices. You use arrays if you know the maximum number of elements and you are happy to reserve memory for it from the beginning, which is typically needed for better application performance. You use slices in all other cases.
In Go (Golang) both slices and arrays are statically typed, meaning that all the elements must be of the same data type. It is possible though to break this using interfaces, which we will cover later in this series, but it is rather not recommended unless you have very strong reason to do so, which you typically don’t
The most common operations you use with the respect to list/slices, which are not applicable for arrays as they change the length:
- Adding new elements.
- Deleting elements.
The most common operations, which are applicable to lists/slices AND arrays, as they don’t change the length:
- Change the value of the existing element.
- Sorting elements using some order.
- Reversing elements’ order.
- Looking up if certain element exists.
It is good enough from the theory perspective, as all the things we are practically oriented, so let’s see how all that works.
Examples
The scenario, which you will see now both in Python and Go (Golang), relies on the previous blog post, where we introduced how to read variables from environment in Linux or MAC. On top of that we build the following:
- All the input variables are passed as a single comma-separated string of variables
- Some function is used it to separate string into a list/slice
- Apply all the operations we’ve mentioned above to the list/slice.
- Convert the list/slice back into a sting.
Python
The Python code for this scenario is the following:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 """From Python to Go: Python: 004 - lists and slices"""
# Import part
import os
# Functions
def get_automation_input() -> str:
"""Helper function to get the automation input"""
return os.getenv("AUTOMATION_INPUT")
# Read environmental value
automation_input = get_automation_input()
print(f"{automation_input=}")
# Create a list from string
automation_list = automation_input.split(",")
print(f"{automation_list=}")
# Add an element to the end of the list
automation_list.append("new_device")
print(f"{automation_list=}")
# Add an element to the beginning of the list
automation_list.insert(0, "provisioning_required")
# Check if that element is in the list
if "provisioning_required" in automation_list:
print(f"provisioning_required is for the device {automation_list[1]}")
# Remove an element from the list
automation_list.remove("new_device")
print(f"{automation_list=}")
# Change the element in the list
automation_list[0] = "provisioning_done"
print(f"{automation_list=}")
# Remove the element by index
del automation_list[0]
print(f"{automation_list=}")
# Sort the list
automation_list.sort()
print(f"{automation_list=}")
# Reverse the list
automation_list.reverse()
print(f"{automation_list=}")
# Merger list into a string
automation_string = ",".join(automation_list)
print(f"{automation_string=}")
Each step is commented, so you can see how you can add/modify/delete elements in list Python as well as to do the lookup, sorting and reversing the order. Still, we’ll comment some key things:
- Once the value is read into string from variables, using the method .split() it is converted to a list. The argument to this method is a value of separator, which is “,” in this case.
Method is a function applied to an object and is part of class. We’ll cover object-oriented programming later in this series. Python is OOP-first language, which means that majority of things there are objects.
- To add element to the end of the list, method “.append()” of the list is used with argument containing the value of the new object.
- To add element to the beginning of the list (or to any specific position, e.g. 3-rd position, etc), the method “.insert()” is used, which contains two arguments: index of the element, where element is to be inserted and the value of the element.
- Something new, what we haven’t yet spoken about is a conditional “if“. In this specific case, it is executed if the element exists in list. Under the hood it makes element by element comparison until the match is found: if full match is found, then true is return; otherwise, false is returned.
- To delete the element from the list by its value, the “.remove()” method is used, which takes an argument with the value. It starts from the beginning of the list and delete the first matching element.
- To delete the element by its position, the instruction “del” is used.
- To sort the list in the alphabetical order in ascending manner, the “.sort()” method is used.
- To reverse the order of elements in the list, “.reverse()” method is used.
- To call the specific element, when you want to print it or change its value, you provide the index in the square brackets.
It is worth mentioning, that as methods are applied to objects, in many cases they change the values inside those objects; as such, you don’t need to store the results of methods’ executions. Therefore, all methods applied to the list object changed it internals. It is not always they case though, as method split generated brand new object as the result of its execution. So always refer to documentation.
Here’s an example of the Python script execution:
1
2
3
4
5
6
7
8
9
10
11
12
13 $ export AUTOMATION_INPUT="lab-switch001,ssh,karneliuk,lab"
$ python3 main.py
automation_input='lab-switch001,ssh,karneliuk,lab'
automation_list=['lab-switch001', 'ssh', 'karneliuk', 'lab']
automation_list=['lab-switch001', 'ssh', 'karneliuk', 'lab', 'new_device']
provisioning_required is for the device lab-switch001
automation_list=['provisioning_required', 'lab-switch001', 'ssh', 'karneliuk', 'lab']
automation_list=['provisioning_done', 'lab-switch001', 'ssh', 'karneliuk', 'lab']
automation_list=['lab-switch001', 'ssh', 'karneliuk', 'lab']
automation_list=['karneliuk', 'lab', 'lab-switch001', 'ssh']
automation_list=['ssh', 'lab-switch001', 'lab', 'karneliuk']
automation_string='ssh,lab-switch001,lab,karneliuk'
Try to replicate the example and experiment further.
Go (Golang)
Let’s now implement the very same scenario in Go (Golang):
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 /* From Python to Go: Python: 004 - lists */
package main
// Import
import (
"fmt"
"os"
"slices"
"strings"
)
// Aux functions
func getAutomationInput() string {
/* Helper function to get the automation input */
return os.Getenv("AUTOMATION_INPUT")
}
// Main functions
func main() {
// Get the input
automationInput := getAutomationInput()
fmt.Println(automationInput)
// Create a slice
automationSlice := strings.Split(automationInput, ",")
fmt.Println(automationSlice)
// Add new element to the end of the slice
automationSlice = append(automationSlice, "new_device")
fmt.Println(automationSlice)
// Add new element to the beginning of the slice
automationSlice = slices.Insert(automationSlice, 0, "provisioning_required")
fmt.Println(automationSlice)
// Check if the element exist in the slice
if slices.Contains(automationSlice, "provisioning_required") {
fmt.Printf("provisioning_required is for the device %v\n", automationSlice[0])
}
// Remove elemement from the slice and by index
deleteIndex := slices.Index(automationSlice, "new_device")
automationSlice = append(automationSlice[:deleteIndex], automationSlice[deleteIndex+1:]...)
fmt.Println(automationSlice)
// Change element
automationSlice[0] = "provisioning_done"
fmt.Println(automationSlice)
// Sort the slice
slices.Sort(automationSlice)
fmt.Println(automationSlice)
// Reverse the slice
slices.Reverse(automationSlice)
fmt.Println(automationSlice)
// Merge list into the slice
automationString := strings.Join(automationSlice, ",")
fmt.Println(automationString)
}
As with Python, we’ve added a lot of comments to the code for you to ease its understanding. And as with Python, we provide some further comments:
- One big difference is that you can see between Go / Golang and Python is that Go is not really object oriented. Where you would have methods in Python, in Go, in majority of cases, you see functions with multiple input arguments, with one of the arguments being the variable you want to modify. The vast majority of functions associated with slices are available in a standard package “slices“.
- Names of functions in many cases are the same as you have in Python methods, which make sense as those are English words describing an operation.
- In certain cases, you need to save the result of the function execution (e.g., append() function when you add element or elements to the slice, slices.Insert() to add element to a slice at any position, slices.Index() to find a position of an element). Function append() takes two argument: first is the slice, where you add elements to, and the second is an element (in case we add another slice, we need to unpack it to elements using “…” operator).
- Some functions don’t require for you to store the result of their execution (e.g., slices.Reverse() or slices.Sort()) as they change the content of the array associated with the slice and therefore the changes are reflected in the original slice as well.
- The biggest difference between Python and Go/Golang is how to delete the element from slice. In Go you cannot delete element that simple as slice is associated with an array, which has a fixed length. So what you do in essence is that you create a new slice pointing to new array, which is created without the element you want to delete and then garbage collector in Go frees memory associated with the array.
- When you provide in square brackets not a single index, but two index separated by column, that refers to a sub-slice (or slice of list in Python). If you use column, but provides only a single index, depending where the column is with the respect to index, it either means that you query of the elements from the beginning of the list to your index (e.g., [:5]) or from the current index till the end of the list (e.g., [5:])
Let’s see the execution of the example:
1
2
3
4
5
6
7
8
9
10
11 $ go run .
lab-switch001,ssh,karneliuk,lab
[lab-switch001 ssh karneliuk lab]
[lab-switch001 ssh karneliuk lab new_device]
[provisioning_required lab-switch001 ssh karneliuk lab new_device]
provisioning_required is for the device provisioning_required
[provisioning_required lab-switch001 ssh karneliuk lab]
[provisioning_done lab-switch001 ssh karneliuk lab]
[karneliuk lab lab-switch001 provisioning_done ssh]
[ssh provisioning_done lab-switch001 lab karneliuk]
ssh,provisioning_done,lab-switch001,lab,karneliuk
Lessons in GitHub
You can find the final working versions of the files from this blog at out GitHub page.
Conclusion
Starting from this blog post, as we have promised in the previous one, you start seeing the difference between Python and Go/Golang beyond the simple syntax. They are different in the very nature: with Python being object-oriented programming language and Go being procedural-oriented programming language; you should think different when you develop applications with them. Take care and good bye.
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