Exploring Golang as a Python developer

Debojyoti Pramanick
9 min readJul 11, 2021

Recently I have been bitten by the Go bug just like a lot of folks.

While trying to learn it I have invariably compared it with what I am familiar with, i.e python, trying to spot the similarities and dissimilarities alike, thinking about what unique features one provides over the other.

In this article, I try to deconstruct how to make sense of a Golang code taking advantage of the fact that we know how it is written in Python.

We will take a problem, understand its nuances and write it in python first. We will then look at the equivalent code in Golang and try to decipher the different parts to it. Along the way we will learn the different constructs used in a typical Go program.

What is the problem we solve ?

The Balanced Brackets Problem

The Balanced Brackets Problem — Given a string expression made up of brackets write a program to determine if the pairs and order of brackets are correct in the string. The brackets to look for are “(“, “)”, “[“, “]”, “{“, “}”, and the string can have other optional characters.

The string expression will be an input to the function and it should return a boolean indicating whether the string is balanced or not.

Example —

Input to function — “[()]{()()[()]}”
Output by function — boolean “True”
Reason — All brackets are balanced

Input to function — “[(){]()]”
Output from function — boolean “False”
Reason — The opening bracket “{“ is is incorrectly followed by a close bracket “]”

Lets take a moment to think of how to solve it. What comes to your mind when you think about a possible solution ?

Any particular data structure or programming construct that comes to your mind ?

Thinking time …

How do I remember if I had seen something before ?

Congratulations if something like “stack” or “recursion” came to your mind :-) This is a classic problem where we can use the stack data structure and follow the following steps :

  1. Push/add to stack if we see an opening bracket.
  2. Pop from stack if we see a closing bracket and compare it with the element popped. It should be the corresponding opening bracket.
  3. Ignore if any other character encountered.
  4. Stack should be empty in the end.

Now lets look at the python code :

Few things to note related to the code :

  1. A “brackets_map” dictionary is the mapping between each start_bracket and close_bracket.
  2. lists in python are a good abstraction for stacks, append and pop methods of list pushes/adds element to and pops element from the end of the list in O(1) time and space.
  3. Length of stack should be checked before popping an element (as empty stack will give an out of index exception)

An aside on queues in python
Although a list is a good abstraction for a stack, it is not an optimal data structure to implement a queue. The reason is, popping from front of a queue will have a time complexity of O(n), as we remove from start of list — queue.pop(0), and shift all elements after that.
Explore the collections.deque or queue.Queue modules for a better queue.

Go Time

We will now proceed to look at the equivalent Go code for the program. Before looking at it, please bear the following in mind :

  1. The Golang code might look longer than the python code.

The code might not look exactly “elegant” to people who have spent a long time with python but haven’t started their journey with Go. However working with Go over a time will eventually unravel its true power and “elegance” (and why it is becoming so wide spread in industry). Even Guido Van Rossum in a recent interview acknowledged that Go is the most “pythonic” of all recent languages. Such a remark from the creator of python certainly counts, doesn’t it. :-)

2. Talking of creators, Go is created by Robert Griesemer, Rob Pike and Ken Thompson, who are stalwarts and pioneers in the field of computing.

To add, projects like Kubernetes, Docker and Mongo DB are built on Golang.

3. Go is a strongly typed language and will require you to declare the types for all the variables you will be creating.

4. Go might “seem” to be missing a few operators python provides out of the box, so be prepared mentally to write them out :-)

Enough talk, Show me the code :

What are the things that you found similar to python, and what are the dissimilar parts ? Try to map the features you know in python with an equivalent construct in Go. The similarities are easier to spot.

In the next section we try to make sense of the dissimilarities.

Stranger things :

1. The data structures — slices and maps

We need to understand the data types “slice” and “map” in Go.

Roughly, a “slice” in Golang is an equivalent of a python “list” and “map” is the equivalent of a python “dictionary”.

Before we understand slices, just a word about arrays :

array

arrays hold collection of values that have the same type. It can only hold a fixed number of elements (which are specified during the time of declaration).

var odds = [5]int{“1”, “3”, “5”, “7”, “9”}

The above declaration is an example of an array holding 5 integers.

The fixed number of elements constraint makes arrays quite limiting, and they are not seen much in real world. It is “slice” that is used more.

slice

The slice data structure is more like a “list” in python in that it can be resized dynamically as required. Note though, unlike lists in python, it holds data of a single type as specified during declaration.

A slice is declared just like an array but does not have the size specified. It can grow or shrink as per requirement.

var my_slice_str = []string{"balanced", "brackets", "problem"}

The above declares and initialises a slice of strings using the slice literal, with 3 strings specified.

To put it more accurately, slices are views to an underlying array, but it is out of scope of our discussion.

map

A map is equivalent to a dictionary in python, wherein we specify key value pairs and a key helps us to reach a value. Declaration of a map specifies the key type and the value type.

my_map = map[string]int {   “key1”: 1,   “key2”: 2,   “key3”: 3,} 

The above declares and initialises a map which has the string type as key and integer type as value.

Note: slices and maps can also be created alternatively with the “make” keyword, we omit it from our discussion.

2. The declarations looks a bit different ?

Variable declarations in Go can take one of the below following forms.

a. Declare a variable with a data type, but don’t initialise it:

var i int

The above declares a variable with data type int

b. Declare a variable with a data type and initialise it at the same time :

var i int = 5

In our Go code, the bracketMap variable is declared in this way.

“bracketsMap” is declared to be a map which has a rune as key and a rune as value. (“rune” is the type for characters in Golang, we discuss it in the next section)

var bracketsMap = map[rune]rune{  ‘(‘: ‘)’,  ‘{‘: ‘}’,  ‘[‘: ‘]’,}

Both the above types of declaring a variable are called “long variable declarations” wherein we explicitly specify the ‘type’ of the variable and might (or might not) initialise it.

c. Declare a variable with short variable declaration operator:

i := 5

:= is called the ‘short variable declaration assignment’ in Go, it does the declaration and assignment at the same time, and infers the data type from the value.

In our Go code, the stack variable is declared in this way.

stack := []rune{}

stack is defined as a slice of runes.

3. The “rune” data type

This brings us to a strange looking data type — “rune”

“string”, “int”, “map” as data types sound all too familiar, but what is “rune” ?

To put very simplistically, “rune” is an equivalent of single character, among many characters, that make up a string. In python, we do not have a separate “char” type, all single characters are strings of length 1.

stack := []rune{}

The above declaration defines a slice of runes, i.e, a slice of single characters (i.e, an array of single characters declared without a specific size, so that the array can grow/shrink dynamically)

An aside on rune

In “Go” we have a distinct “rune” type, which represents unicode code points and is just an alias for type ‘int32’. A separate type of “int32” distinguishes it (as a code point) from type “int” which is the type for normal integers. As an example, the rune literal ‘a’ is actually the number 97.

The rune/int32 covers all characters from 1 Byte to 4 Bytes. A character in ASCII range will take just 1 Byte, some other unicode character in other languages might take from 2 to 4 Bytes.

A rune literal is specified in Go with a single quote:

// declare and initialise 2 runesch1 := ‘a’ch2 := ‘ќ’fmt.Printf(“Type: %T, character: %c, code point: %d \n\n”, ch1, ch1, ch1)fmt.Printf(“Type: %T, character: %c, code point: %d \n\n”, ch2, ch2, ch2

Output :

Type: int32, character: a, code point: 97Type: int32, character: ќ, code point: 1116

A very important distinction between strings and characters in Go is that strings are made bytes and not runes/characters. Strings, bytes, characters and encoding/decoding might sound simple but a lot goes in the background. You are encouraged to check the details on the inner workings below : https://blog.golang.org/strings

4. “if” statement looks a bit different too ?

if ch_type := getBracketType(ch); ch_type == "openBracket" {   
stack = append(stack, ch)
}

A statement before a condition, but we don’t see that in python, right ?

Go allows conditional / loop expressions to be preceded optionally by a statement. It is evaluated before the condition expression, and the scope of ch_type here is limited to the “if” statement.

5. Go does not have an inbuilt stack data structure

Bad news, we can’t just invoke something like a list in python and use it as a stack.

Good news, we can initialise a stack as a slice of ‘runes’ and have ways to append to the end of the stack and pop the last element from it.

stack := []rune{}

Push : An element can be pushed to stack using the built-in append function.

stack = append(stack, ch)

Pop : Alas, there is no inbuilt “pop”, but the top element can be easily removed by slicing it off.

stack = stack[:len(stack)-1]

6. Go does not have an “in” operator

Go does not have an equivalent of “in” operator in python, either ! (Arghh Go! you are making me work too hard !!).

We can write a function to identify if a character is an “openbracket” or a “closedbracket” utilizing the bracketsMap ‘map’ we had initialized earlier.

func getBracketType(ch rune) string { 
for k, v := range bracketsMap {
if ch == k {
return "openBracket"
} else if ch == v {
return "closeBracket"
}
}
}

Function signature in Go :

func getBracketType(ch rune) string { ... }

Functions start with the keyword func, followed by function’s name. The inputs to the functions are defined inside the braces next (we have only one input — variable “ch” of type “rune”), and the return type comes at the end.

Also, observe the “range” keyword in Golang has a slight resemblance to “range” in python. However, unlike accepting integers / range of integer as argument as in python, it is used to iterate over elements in data structures like maps, slices and arrays.

“range” on a map returns 2 values each time around the loop — the key and its corresponding value in the map.

7. ‘Underscore’ works the same as in python

We end the article with a similarity with python, the “underscore”. Remember the section of code where we saw it ?

for _, ch := range ipStr 
{
if ch_type := getBracketType(ch); ch_type == "openBracket" {
stack = append(stack, ch) }

What if we don’t use one of of the variable that the loop returns in every iteration.

The underscore “_” , also known as the blank identifier in Golang, has the exact same utility as in python — to ignore variables that are not going to be used in the program.

Just to add, Go will throw a “declared but not used” error for unused variables in your program. Hence using a blank identifier becomes useful tell the compiler that this variable is not needed and to ignore it without error. What more, it makes the program more readable.

Conclusion

In this article, we saw how to get our feet wet with “Go”, solving a classic problem, discussing some constructs about the language and leveraging our knowledge of python.

Thanks for staying this far. I will be following this up with more articles where we get into more features on programming languages.

Clap, share if you liked the post.

--

--