After doing Python for the past 10 years, Go seemed totally foreign. But a few days and a few hundred online searches later, I had finally cobbled together my first “real” Go program. The one thing I wish I had was a 1:1 guide mapping Python language concepts to Go... and not just the equivalent syntax, but the correct way of expressing these same ideas in Go.
This is not going to be a tutorial, but more of a guide showing the most common Python idioms and their equivalent in Go:
Table of Contents
Culture
Python programmers go by the demonym “Pythonistas”. Go programmers are “Gophers”. So don't get a big head once you’ve mastered Go - you’re still in the animal kingdom!
While both are open source languages, Python is totally owned by the community and run by the non-profit Python Software Foundation (PSF). Go is owned and run by Google, and was designed to serve Google’s needs. This information is not relevant to your own development, but it is always good context to bear in mind when reading through mailing lists, forums, etc.
Naming Conventions
Python follows a few style conventions:
- Functions and variables are snake case
snake_case
. - Classes are Pascal case
PascalCase
. - Globals or “constants” are often all caps
CONSTANT
. - Private members begin with an underscore
_private
.
Golang has a few different, simpler rules:
- Anything that begins with a lowercase letter is private.
- Anything that begins with a capital letter is public.
- Depending on public or private, everything follows
camelCase
(private) orPascalCase
(public).
Python:
# A public "constant" that should not be changed.
MY_CONSTANT = "Apple"
# Public function.
def do_thing():
return "Banana"
# "Private" function.
def _do_thing():
return "Cherry"
# A public class.
class MyClass:
pass
Golang:
// A public constant that cannot be changed.
const MyConstant = "Apple"
// Public function.
func DoThing() string {
return "Banana"
}
// Private function.
func doThing() string {
return "Cherry"
}
// A public struct.
type MyStruct struct {}
Comments & Docstrings
Python has one primary comment delimiter, the octothorpe #
. Likewise, Go’s
primary comment delimiter is the double-slash //
. Go also supports multi-line
C-style comments /* */
, but they are seldom used.
Python also has a special docstring which is the usage of a triple-quote string directly below a class or function definition. The Golang equivalent to a Python docstring is an ordinary comment placed before the function definition, starting with the function’s name.
Similar to Python, docstrings are applicable to functions, global/constant variables, and classes (structs in Golang). Basically, anything that is not indented should have a docstring. And similar to Sphinx, Go tools will generate human-readable HTML documentation based on these comments.
Python:
def add_numbers(a, b):
"""
Adds numbers together and returns the sum.
Docstrings can also be multi-line as so.
"""
# This is a comment. Adds the numbers.
return a + b
Golang:
// AddNumbers adds numbers together and returns the sum.
//
// Comments can also be multi-line as so.
func AddNumbers(a int, b int) int {
// This is a comment. Adds the numbers.
return a + b
}
Variables
The biggest difference in Go syntax versus Python is how variables are
defined. This primarily relates to the assignment (=
and :=
) operators.
Python:
# Define a string variable.
fruit = "Apple"
Golang: In Go, we first have to define the variable, before we can assign to it. This is essentially "long form" Go.
// Define a string variable.
var fruit string
fruit = "Apple"
This can be condensed into one line:
// Define a string variable.
var fruit string = "Apple"
And this can be condensed even further with the ”short assignment” operator
:=
. This operator infers the type of the object, and is very heavily used in
Go code due to its conciseness.
// Define a string variable.
fruit := "Apple"
Be aware though, that once a variable is first defined, you only use the normal
assignment operator =
to change its value afterwards.
// Define a string variable.
fruit := "Apple"
// Change its value.
fruit = "Banana"
fruit = "Cherry"
String Formatting
Python has three ways of doing string formatting: C-style %
operator,
.format()
, and the newer f-strings f""
.
Golang uses the traditional C-style formatting syntax, which is provided by the
fmt
package in the standard library.
Python:
name = "Monty"
age = 40
# C-style.
info = "%s is %d years old" % (age, name)
# Format.
info = "{0} is {1} years old".format(age, name)
# F-string.
info = f"{name} is {age} years old"
Golang:
import "fmt"
name := "Monty"
age := 40
info := fmt.Sprintf("%s is %d years old", name, age)
Similar to Python, Golang format strings can handle various types (strings, ints, etc.) and can do various things such as padding, decimal point precision, etc. Refer to the fmt package documentation. The overal formatting features are not nearly as robust as what you get with Python, but Go provides enough basics for doing console and log output.
If you need really advanced string formatting, Go provides a templating language in the standard library, which is very similar to Jinja.
For Loops
The for loop in Python has super simple syntax compared to other languages. The
behavior of Python’s for loop (for x in y
) is often referred to as a for-each
loop in other languages. The for loop in Go can behave like a for-each using the
range
keyword which will iterate over the contents.
range
behaves similar to Python’s enumerate
function, which returns both
the current index number and the object.
Python:
# A list of strings.
fruits = ["Apple", "Banana", "Cherry"]
# Print each fruit with its index.
for (index, fruit) in enumerate(fruits):
print(f"Fruit {index} is {fruit}")
# Print each fruit.
for fruit in fruits:
print(fruit)
Golang:
import "fmt"
// A list of strings.
fruits := []string{"Apple", "Banana", "Cherry"}
// Print each fruit with its index.
for index, fruit := range fruits {
fmt.Printf("Fruit %d is %s\n", index, fruit)
}
// Print each fruit. Here we are throwing away the index by assigning it to _
// which is a special variable in Go used for ignoring the output.
for _, fruit := range fruits {
fmt.Println(fruit)
}
Lists
Slices in Golang are equivalent to Lists in Python. Golang also has more traditional arrays, like you’d see in C, but when starting out just focus on slices.
Python:
# Define a list.
# The trailing comma after "Cherry" is not required, but recommended.
fruits = [
"Apple",
"Banana",
"Cherry",
]
# Zero-indexed random access.
apple = fruits[0]
cherry = fruits[2]
# Negative index are also supported.
cherry = fruits[-1]
# Slices give you a subset of the list, using colons.
apple_banana = fruits[0:2]
apple_banana = fruits[:2]
# Append to the list.
fruits.append("Durian")
# Combine two lists.
veggies = ["Tomato", "Lettuce"]
foods = fruits + veggies
Golang:
// Define a slice.
// The trailing comma after "Cherry" is required or it won't compile!
fruits := []string{
"Apple",
"Banana",
"Cherry",
}
// Zero-indexed random access.
apple := fruits[0]
cherry := fruits[2]
// Golang cannot do negative indexes. So you'll have to count backwards instead.
cherry = fruits[len(fruits)-1]
// As the name indicates, you can take slices of a slice.
apple_banana := fruits[0:2]
apple_banana = fruits[:2]
// Append to the slice. The append function in Go actually returns a new slice.
fruits = append(fruits, "Durian")
// Combine two lists.
// Here we are using the special variadic syntax `...` which has a similar
// behavior to the commonly used `*args` in Python.
veggies := []string{"Tomato", "Lettuce"}
foods := append(fruits, veggies...)
// This is equivalent to fully exploding the veggies slice and appending each
// element separately.
foods = append(fruits, "Tomato", "Lettuce")
Dictionaries
Maps are the Golang equivalent to Python dictionaries. Functionally they are very similar, as maps provide instant access to a value by a key. And similar to dictionaries, keys and values can be any object type, and maps can be nested.
The one difference is that there is no equivalent to Python’s update()
in Go,
however a simple 3-line for loop can accomplish the same thing.
Python:
# Define the dictionary.
fruits = {
"a": "Apple",
"b": "Banana",
"c:" "Cherry",
}
# Access.
word = fruits["a"]
# or...
word = fruits.get("a", "Fallback value")
# Assignment.
fruits["d"] = "Dragonfruit"
# Looping/iterating over a dictionary.
for key in fruits:
value = fruits[key]
print(f"{key}: {value}")
# Updating or merging two dictionaries together.
fruits.update({
"c": "Cantaloupe",
"d": "Durian",
})
Golang:
// First we must allocate a new map with using ``make()``,
// declaring key & value types. The syntax here is:
//
// map[key]value
//
var fruits map[string]string
fruits = make(map[string]string)
// As a shortcut, you could instead allocate it with empty contents.
fruits = map[string]string{}
// Or you can allocate one with pre-defined contents.
fruits = map[string]string{
"a": "Apple",
"b": "Banana",
"c": "Cherry",
}
// Access.
word := fruits["a"]
// Assignment.
fruits["d"] = "Dragonfruit"
// Looping/iterating over a map.
for _, key := range fruits {
value := fruits[key]
fmt.Printf("%s: %s", key, value)
}
// Updating or merging two maps together.
// There is no direct equivalent to Python's `update()` in Go,
// but you can accomplish it with a simple loop.
updateFruits := map[string]string{
"c": "Cantaloupe",
"d": "Durian",
}
for _, key := range updateFruits {
fruits[key] = updateFruits[key]
}
Classes
Go is not object oriented, and therefore does not have classes. This is a big shift from thinking in Python. However, Go does have the struct which is essentially a very simple class used to hold data. By combining structs with functions, you can approximate similar feature parity with Python classes.
After adjusting to the Golang way of using structs with functions, I actually prefer it as it is even simpler than Python classes, albeit less flexible. It actually feels more Pythonic than Python in some respects!
Python:
class Fruit:
"""
Holds nutrition facts about a fruit.
"""
def __init__(self, name, sugar, calories):
self.name = name
self.sugar = sugar
self.calories = calories
def take_bite(self):
"""
Taking a bite of the fruit reduces the calories by 10.
"""
self.calories = self.calories - 10
# Create a Fruit instance.
apple = Fruit(
name="Apple",
sugar="96 grams",
calories=100
)
# Take a bite!
apple.take_bite()
Golang:
// Fruit holds nutrition facts about a fruit.
type Fruit struct {
Name string
Sugar string
Calories int
}
// TakeBite reduces the calories of the fruit by 10.
//
// Note that instead of `self`, we are providing the function definition with a
// reference to a `Fruit` object `f` (indicated by the asterisk `*Fruit`).
func (f *Fruit) TakeBite() {
f.Calories = f.Calories - 10
}
func main() {
// Create a Fruit instance.
apple := Fruit{
Name: "Apple",
Sugar: "96 grams",
Calories: 100,
}
// Take a bite!
apple.TakeBite()
}
Keep in mind, Go is not object oriented. Therefore there is no direct equivalent
to Python class inheritance, super()
, reflection/introspection, etc. If you
think in terms of parent-child objects, your biggest challenge will probably be
in how to shift your thinking to structs and functions.
Next Steps
Hopefully this guide will help as you attempt to re-wire your Python brain. I would recommend first following the guides in the Getting Started section of the Go docs. The best way to get started after that is to dive right in and start writing a little command line tool or web app.
The best way to pick up the general “feel” of Go is to read through some of the standard library code and comments. I would also highly recommend reading Effective Go once you get past your first Golang tutorial. It really clarifies all of the language’s conventions and best practices.
Go has many other analogs to Python such as datetimes, file handling, internet protocols, etc. But once you know the basic conventions and built-ins described above, you will start picking up Go’s other functionality in no time.
Happy gophering!