The world’s leading publication for data science, AI, and ML professionals.

From Python to Go

Learn Go Programming by Translating Python Code

My hope is that this article can be used both as a dictionary for Pythonistas who want to learn Go but also as an introduction to Go for those out there who know Python and just want to get started quickly with Golang.

Before we begin, I want to briefly explain why it might be a great choice to learn Go when you already know Python.

Intro

First of all, I am not saying that Go is a better language than Python. Python is a great language and often a better choice than Go. It depends of course on the problem you are trying to solve or the application that you want to build, but Go does have a lot of things going for it that Python doesn’t.

  1. Go is fast – really fast! With almost C-like speeds, Go blows Python out of the water on this parameter. That is partly because Go is a statically typed language with an optimized, extremely fast compiler which makes Go both fast to develop in and fast at execution.
  2. Developer-friendly. Despite the swiftness of Go, it is surprisingly fast to write in. I think there are a couple of reasons for this. First of all, in Go you only have what you really need, so you are rarely in doubt about what to use where. The syntax is clear and readable. Even syntactically it is cleansed from ugly parentheses and other unnecessary syntaxes. Secondly, the Go interpreter is pretty smart and Go’s type system is a nice balance between the lack of unnecessary declarations and a save system guarding for unused imports and unstated datatypes in function arguments for example.
  3. Built-in concurrency and parallelism. Go is a young and modern language and if you have ever tried running bigger projects with some parallel computing in Python, you know that it is a pain. But in Go, it is not. You have green threads called goroutines and you have channels which is a data structure that makes parallel workers communicate rather safely with each other. You also have a way of locking containers like arrays to avoid race conditions (if the default blocking nature of channels isn’t enough) and of course a way to prioritize which goroutine to run as well as a synchronization feature known as a waitgroup.
  4. Low level. Even though Go is a developer-friendly, multi-purpose Programming language it is very low level. That is, you have a lot of control over your machine. Some examples of what you can build in this performant and low-level language are Docker and Kubernetes. Think about the syscalls you have to make just to create a Docker container!

Go!

In this article, we shall embark on a syntactic journey from one language to another. That being said, I will also discuss style and structure, for as you all know, a direct translation line by line, is a bad one no matter the languages.

Note that this is a short article and certain features of Go have been skipped. That being said, I have tried to include the most important parts so that this is as self-contained as possible and so that you will get a full picture of the language and actually learn some Go.

Without further ado, let’s jump right into it, by talking briefly about imports, data types, and the declaration of variables.

Start

The first thing that stands out in Go when you come from Python is that in every go file you need to specify what is called a package. I won’t go into detail about the packages and the structure of Go projects because this article is about syntax, but it suffices to say that if two files are in the same package, then they have access to the content of one another.

So you start by declaring a package and if you want to have your starting point of execution in your file, then you should name that package main. The main keyword is a special word in Go meaning "this is where the program starts".

Functions

The second thing alien for Python programmers is that you don’t need to actively state which function to run because, in Go, you always run the function called main. That also means that you have to create such a function in Go if you want to run anything.

Before you create functions though, you have an option to import stuff. This is finally something that you should recognize from Python.

Let’s look at some "Hello World" programs in each language to see what a possible start looks like.

The Python file needs no comments really. The only thing interesting in comparison to Go is that you need to specify which function you want to run and you don’t need to import anything.

The Go file however is a lot different already. You specify a package (you will get an error if you don’t) and you need to import a _format-_lib to do IO action. You also need to create a function called main, that will run no matter what and functions in Go are created using the func statement where of course we use def in Python.

To run the Go file called "main.go", you simply open up a terminal or shell environment, make sure you are in the folder where the file is, and type:

go run main.go

The Go functions need to be told which data types to expect as input as well as output.

func calc(number int) float64 {
...
return result
}

This is in many ways a good thing because it avoids many errors. It is simply safer.

Now that we are at the shell, terminal, cmd, powershell or whatever you prefer to use, there are some Go commands that you should know.

  • go run will compile and run your file.
  • go build will compile your file and build an executable that you can then share with your friends and delete their harddrive 🙂 (or maybe just make a game if you wanna keep your friends).
  • Go get is yet another nice command that lets you download Go packages directly from an online source like Github.

There are of course many more nice Go commands but I won’t spoil all the fun.

Data Types and Everyday Syntax

Both languages have the usual number types like int, float, etc., so I won’t go through them all. There are some data types, however, that we need to look at.

Strings

Strings in Go are much like Python’s but many of the operations needed for strings will be found in a library you need to import. Namely, the strings library.

Slices and Arrays

In Go, there are two main containers that resemble Python’s lists. Arrays and slices. Arrays have a prespecified number of elements and can be used when you know how many elements you need to store in the variable. Moreover, there can only be one datatype through the array.

Slices are more flexible and are more like Python’s lists. They are mutable and can store any number of elements. But they differ from Python lists in that they also can only store one kind of datatype. There is however a way out of the one-datatype problem that I will address later.

Let’s visit the syntax.

Note that we use the shorthand declaration and instantiation using the symbol :=. This is fine in many cases but only inside a function.

You can declare variables in outer scope but that syntax is different. You would need to do something like

var b uint = 5

When you declare a variable like this, you have more control over your data type. Here I have declared an unsigned int (meaning a non-negative integer). I could not do that using the shorthand above because Go would have thought that b was of type int.

You can try to run the above snippet. You will see that we print out the type of the array and the value of the slice respectively.

Once you have declared say, a slice, there is of course a lot of syntax associated with them. A lot like in Python’s syntax actually.

Sometimes we say that s is a "slice of string" to be explicit about the data type.

Now of course Python has more container types. For example, what’s the equivalent of Python’s dict?

Maps

Like many other languages, Go chose to call their hashmap map.

They are much like Python’s dicts.

There are a few ways to create a map in Go, but once created, it will be very familiar to Pythonistas.

Loops and Control Flow

In Python, we have of course the for loop and the while loop. Go aimed to be a little more restrictive and so they only have one syntax (more or less) and only one loop type. The for loop.

But Go’s for loop is different from Python’s in that it needs to be stopped or else it simply won’t and then you have a while loop.

Let’s compare some code in the two languages.

I will add a little more to the Go file, to explain some syntax.

I believe several comments are in order here. Note that Go uses curly brackets instead of indentation and also that there are no parentheses like in C-like languages. You also see here how to write an if-else clause in Go, note also here that there are no parentheses and the else keyword should be between two curly brackets.

When we iterate over an iterator like numbers above, Go has a keyword called range that makes this possible and returns an index by default. Go supports multiple outputs and you need to learn the basics of when and where Go outputs what.

The rule of thumb is that indexes are outputted to the left of the value and bools and errors are outputted to the right. You can just keep this in mind as we move on with the examples.

If you are not using the index of the elements then you just use an underscore in its place.

for _, number := range numbers {...

In the above snippet with the loops, we used an empty for-loop as a "while True" in Python. In Go, you can also use a boolean type (called bool in Go), so it becomes a true while loop.

for someBool {...

Go also have an else-if statement equivalent to Python’s elif of course and the syntax is

} else if ... {

Switch

The switch statement in Go is very useful when you have multiple possibilities for a value and you need to do effective control flow in your program. In Python, you have to do a lot of if-elif-else statements but in Go you can do the following.

This syntax is almost self-explanatory. The default block will run if no other choices happen and is similar to "else". It is not mandatory and you can skip it as you see in the top switch block. Note that Go has borrowed the colon and indentation from Python here to make it more readable.

Error Handling

In Go, error handling is very different from many other languages. I personally like it, but not all do.

An error in Go is usually returned from a function. So the following is a common pattern to see

You can of course print the error message or of course make go shut down in a so-called panic. The line

panic(message)

will print out the message and shut down the program.

Pointers

Go is a low-level programming language. This means that you have great control over your computer. In particular, you can access the memory addresses with pointers. This has many advantages. It is especially useful when you need to do a permanent update to a datatype inside say, some function or method (more about that later).

The syntax of pointers is easy and very much like in C.

In the following example, we create an int a and get the memory address of it and store it in a variable b.

This code will print out something like

0xc0000140b8
5

Note that we use the dereferencing operator (asterisk) * to get back the value that the memory address holds.

Structs and Methods

Python is, of course, an object-oriented programming language i.e. it has support for classes and instantiations of these called objects. In fact, in Python, almost everything is an object. From functions to strings and generators – they are all objects. If you don’t believe me, you can try calling the built-in function dir on some piece of data, print out the output and you will see the attributes and methods you can call on the given object.

In Go, things are quite different.

Go is not an object-oriented language and therefore it has no classes, class-methods, or objects. It does however have equivalents as you will see now.

Go has Structs. Like in C, these resemble Objects in JavaScript and in a sense classes with only fields in Python.

The basic syntax is as follows:

You create a struct in Go using the type keyword and the idea is like in the case of classes in Python that you can make your own data types.

Like classes, Go offers methods as well and they are very much like Python’s methods except that they are not defined inside the struct but outside the struct.

Let’s see them in action. Add the following to the above.

You can see that the way we define methods in Go is much like functions except that we define which struct it belongs to in parenthesis (in the definition) before the arguments.

Note also that it accepts a pointer type and not an instance of the struct itself. If I had removed the asterisk and simply created a method of the form

func (p person) setSkill(skill string) {
p.skills = skill
}

then this wouldn’t work. The reason is simple. I want to change the instance of the struct permanently! I don’t want to just get the changed value and leave it unchanged. I could of course do this but it would not change the struct for good. Instead, if I change the value at the memory address (the place that the pointer points) then the instance of the struct gets the value changed until we change it again.

Then you may ask, why (on line 12) don’t we need to pass a pointer to the method? We have p and not &p.

Well, actually you could have done this explicitly like

(&p).setSkill("Coding Go")

but the Go compiler is clever enough to understand that what you want is actually a pointer and not the instance of the struct itself, so this is just syntactic sugar for the above parenthesis-ugliness.

This concept of pointers in methods tends to confuse a lot of new learners but you should just keep in mind that if you want to change something permanently then use pointer methods. If you just want to use some value, then use the struct itself.

Interfaces

One of the really nice and useful features of Go is the concept of interfaces. You should think of an interface as a common denominator that glues together structs that have the same kind of methods.

More precisely, an interface is a collection of methods, so that if a struct has exactly those methods then it satisfies the interface and we can use the interface and the struct with its methods interchangeably.

Say that I have a circle struct and a square struct. They both have a perimeter method and an area method with the same arguments and outputs.

Then we could declare a shape interface containing these types of methods.

Let us look at a classical example of this situation and note the parentheses around the packages that we import – this is the syntax for multiple imports.

The point is that even though the function measure takes in a type of shape, it can accept types of rect and circle as well. This concept is known as polymorphism and Go supports that through interfaces.

Have you thought about how the Println function is able to print different types or how the len function is able to calculate the length of different types?

That is because of interfaces.

In Python, of course, this is possible as well since it is an object-oriented language. You could simply access the dunder-method len() and return it.

An example of a very useful interface is the empty interface.

interface{}

Every struct satisfies the empty interface and therefore this interface represents any type.

Remember that I told you that there was a solution to the one-type-only in arrays and slices? The answer is interfaces.

We can make a slice of a certain interface that accepts many types. The following would work.

d := []interface{}{1, "Kasper", 3.2, 'A'}
fmt.Println(d)

Note that the last entry of the slice is a char denoted by surrounding single quotes.

The empty interface is a nice trick to have but not exactly best practice. One should always try to be as restrictive as possible in order to avoid errors. That being said, there might be no other solution.

Concurrency and Goroutines

Parallelizing code in Python is a challenge in many cases.

Now, Go is a modern language with built-in support for concurrency and parallelism. They even have a keyword for it.

It is of course important to be aware of the fact that concurrency and parallel computing are not the same. This could be a whole article in itself. The high-level understanding is enough for now.

Namely that parallel computing is when the CPU does several computations at the same time (using several kernels or threads). Concurrency means that the program switches context between parallel workers (or threads) so that when one worker can’t move on (is blocked by some reason or simply have to wait e.g. for a server response) then the context switches to a ready worker that then picks up where it left off. This doesn’t mean that the workers do anything at the same time.

One can do both things quite easily in Go and in many cases you do both at the same time without the Go compiler even lets you know.

Before we get started down this fascinating road, let us start with something that you have already come across, namely the main goroutine.

The main function that runs no matter what, is actually what is known as a goroutine (also called a green thread in other languages). It just always runs.

You should imagine a Go program like a river where the main thread is the main river, and small branches of the river could spawn or branch out if you will.

The branching rivers are called goroutines and are called with the keyword go

Note how the background strings are printed out in parallel in no apparent order. That is how easy it is to parallelize code in Go.

Of course, this poses a potential danger because of rase conditions, non-secure data transfer, and synchronization issues but Go is intelligently build, so there are of course solutions to all these points.

Channels

Channels in Go is a data type that can transfer data from one goroutine to another in a relatively safe manner.

An example:

In the above snippet, the following happens:

First, we create a channel called messages, then we branch out of the main routine with an anonymous function (like Pythons lambdas) that shoots the message "ping" into the channel. At this point, the channel is blocked. If another goroutine were to try to send a message, the message would end up in a queue waiting for the channel to be empty/read so it can receive another message. You can make channels with so-called buffers that can accept x number of messages before blocking but we won’t go into detail about that.

Then we read the message on line 11 and store it in a variable called msg. Then, of course, we print it out to the console.

There is an important thing to note about the lifetime of a Go program. When the main function finishes, all other goroutines exit as well, even if the functions did not finish and did not get to return anything. To display this, let us take a look at the following.

If you try to run this, you will see that we don’t get a single ping! This is because the main function (goroutine) finishes before the anonymous goroutine gets to print out anything. However, if you wait for it, it will get to print something. The following will print out all the pings.

This is working, but it is not best practice.

In Go we have what is known as a waitgroup. Its purpose is to synchronize all your goroutines, so that when the main function returns, then all your goroutines have returned as well.

This code is much better but it needs some explanation.

  • We have changed the function from an anonymous function to a normal function that doesn’t run until called. We call the function counting.
  • Back in the main function we declare a variable called wg of type WaitGroup. This is basically a counter. When wg.Add(x) is called we add x to the counter, and when wg.Done() is called we subtract 1.
  • The function counting has an argument of type pointer to waitgroup. The pointer type is needed because remember we want to subract 1 from the waitgroup permanently.
  • The defer statement inside the counting function means "do the following as the last action before returning". This is very useful since it can be hard sometimes to know which order things will happen in. In our case, we tell the waitgroup that we are done and the waitgroup counter decreases by 1.
  • After running the function counting as a goroutine in the background, we call wg.Wait() which will wait for the waitgroup counter to become 0 before moving to the next line.

The rule of waitgroups is, every time you run a goroutine you need to add 1 to the waitgroup. Every time a goroutine returns you should subtract 1. Then wait for all to finish with wg.Wait().

There is however another way of doing this without waitgroups.

It turns out that you can loop over a channel, and this will only stop if the channel is closed. So this blocking nature is often used to sync your goroutines. Check out the following code, which will give the same output as above.

Very neat! We give the function counting a channel as an argument and use that channel to communicate back to the main goroutine where we will loop over the channel and print out all the messages sent. Back in counting, we close the channel just before exiting the function, in the main function we now know that the channel has been closed and we can move on.

The select statement

Sometimes you have multiple channels to read from and it can be hard to know apriori which channel is ready to be read. You don’t want to waste any time of course so how do we solve this?

The answer is yet another brilliant invention called the select statement. Take a look at the following example.

The select statement is very much like the switch statement, except that it selects between channels. The above is a good example of when this is useful. There is no reason to wait for the c2 channel when c1 is ready to be read. The result of this is that it only takes about 2 seconds to run this and not 3.

The select statement also has a default case and that can be very useful as well.

Naming Conventions and Public Variables

You might have noticed that I tend to keep variable names quite short as opposed to names in Python for example. This is actually on purpose.

You see, a part of the Go language is simplicity, effectivity, and short names. It is no joke! Of course, as always names should be descriptive but you don’t need to overdo it. Dummy variables in loops for example should be one letter names, function names should be one word, etc. Use shorthand when it makes sense for a reviewer, reader, or coworker.

Also, the variables should be camel case instead of underscores.

There is one important thing though that you need to be aware of. The capitalization of the first letter of a variable matters!

In Go, if you want to export functions and variables in order to access them in other files, they should begin with a capital letter. That’s it. Then you can access them from the outside. If they begin with a small letter, then they are private variables, only accessible in your file or scope.

Where to Go From Here

I would recommend a tour of Go along with a couple of other links.

  • You can find a tour of go here: A Tour of Go
  • Also, Go‘s official website is great: Go
  • The official documentation: Docs
  • Go by example is also great: Examples

I hope that you learned some Go and enjoyed the ride. I am confident that this language will become one of the most popular and sought of languages in the world and already it is quite popular, of course.

It can be a little hard to understand the language sometimes because a lot of the standard library is using interfaces to do all kinds of tricks behind the scene like write bytes to an array in the background for example, but if you keep Going, then you will learn it in a short time.

Happy coding!


Related Articles