3  Syntax Basics

3.1 Introduction

We’ll take a look here at some of the fundamental aspects of the Python syntax. Our aim is not to be exhaustive and replicate the whole python official documentation, but rather to get down the basics that will allow us to start writing simple programs and building from there as we go.
Also: It is fine not to remember everything after the first read, we rather want to get familiar with the language and some of the common idioms – just go with the flow :)

3.2 Basic types and data structures

We have numbers:

1 + 2
3
type(1), type(2)
(int, int)
type(1 + 2)
int

As we see, adding two integers results in an integer. We can also represent non-integer numbers as floats:

1.2 + 1.3
2.5
type(1.2 + 2.3)
float

Also notice:

type(1)
int
type(1.0)
float
type(1.)
float

Also, these three numbers evaluate to equal, as we can check using the equality operator:

1 == 1. == 1.0
True

If we add an int to a float, we get:

type(1 + 1.)
float

Python also has strings:

type("Hi there")
str
"Hi there" == 'Hi there'  # Notice both " and ' can be used
True

Strings are very powerful and have many “methods” associated with them that facilitates manipulating them. For example:

"HELLO World".lower()
'hello world'
"HELLO World".startswith("he")
False
"HELLO World".lower().startswith("he")
True
"HELLO World".endswith("d")
True

There are many more methods defined. Whenever you’re trying to do some operation over a string consider checking first if the method is already there. You can inspect the methods like so:

We can format strings using a handy language construct: f-strings. f-strings allow us to easily format our strings in a dynamical fashion, even executing code inside them. For example:

f"Hello world, I think {1 + 1} = 2"
'Hello world, I think 2 = 2'
f"I think {'THIS INNER SHOUTING STRING'.lower()} should be lower"
'I think this inner shouting string should be lower'

Notice that we alternated ” and ’.

We’ll see more examples of f-strings later on, as they are a super handy tool adding a lot of expresivity to the language.

3.3 Variables

We can store and use variables:

first_name = "Nik"
last_name = "Mamba"
age = 23

For example, to format a string:

f"name is {first_name}, the last name {last_name}. Their age={age}"
'name is Nik, the last name Mamba. Their age=23'

Or with this handy f-string substitution:

f"{first_name=}, {last_name=}. Their {age=}"
"first_name='Nik', last_name='Mamba'. Their age=23"
Warning

We need to be careful with the behaviour of variables depending on their type. We will get back to that in a few paragraphs after we talk about mutability.

3.4 Lists

We can store elements in different kinds of containers.

list is the most common of them. We can store basically anything in them, including repetitions of the same element:

short = ["having fun", 123]
type(short)
list
mixed = ["hello", 1, 2., short, short]
mixed
['hello', 1, 2.0, ['having fun', 123], ['having fun', 123]]

We can access the elements of the list via their indices (starting at 0)

mixed[0], mixed[-1]  # Notice -1: refers to the last element
('hello', ['having fun', 123])

We can take parts of that list, a so-called slice, by indicating a slice of indexes. The slice has the syntax (start, stop, [step]), the step is optional and can be negative. The slice is inclusive on the left and exclusive on the right:

numbers = [1, 2, 3, 4, 5, 6]
numbers[1: -1]  # Start from second until last (not including it)
[2, 3, 4, 5]

Importantly, we can modify a list:

mixed
['hello', 1, 2.0, ['having fun', 123], ['having fun', 123]]
mixed[0] = 100
mixed
[100, 1, 2.0, ['having fun', 123], ['having fun', 123]]

You should think of lists as a sequence of references to other elements (objects). Here’s a practical example:

short = ["python", 1, 2]
long = [1, 2, 3, 4, 5, short, short]
short[1] = "is"
short[2] = "fun"
short
['python', 'is', 'fun']
long
[1, 2, 3, 4, 5, ['python', 'is', 'fun'], ['python', 'is', 'fun']]

Since long is simply holding a reference to short, if we modify short, we’ll see that change propagate to long.

We can add elements to a list using the .append method:

result = [1, 2]
result.append("hello again")
result
[1, 2, 'hello again']

Removing elements is also possible:

result.remove?
Signature: result.remove(value, /)
Docstring:
Remove first occurrence of value.
Raises ValueError if the value is not present.
Type:      builtin_function_or_method
result.remove(1)
result
[2, 'hello again']
result.remove(1)
ValueError: list.remove(x): x not in list
Note

Mutability
In Python some types can be modified (thus we say there are mutable), for example list. Others can’t be modified after creating them, thus immutable, for example tuple.

Tip

Explore the buil-int methods associated to list, there are handy ones, like .sort

Note

Names and Variables can be a bit tricky in Python. If you are interested in some more details about the inner workings, I recommend this talk

3.5 Tuples

Similar to list but immutable, which gives them some performance advantages (that you rarely care about). In practice, rather than because of performance, you should prefer them over lists when care about the mutability (see example below).

sports = ("tennis", "football", "handball")
sports[:2]  # Indexing also works here
('tennis', 'football')
sports[0] = "not gonna work"
TypeError: 'tuple' object does not support item assignment

Sometimes we are forced to use immutable data structures, for example as dictionary keys. That’s a perfect use case for tuples too:

mutable_key = [1,2,3]
immutable_key = (1,2,3)
{mutable_key: "not gonna work"}  # Try to build a dictionary
TypeError: unhashable type: 'list'
d = {immutable_key: "that looks better"}

And we can retrieve that value as per usual:

d[(1,2,3)]
'that looks better'
d[1,2,3]  # optionally without parenthesis
'that looks better'

In a subsquent chapter we will cover a more useful variation of tuple, the NamedTuple.

3.6 Sets

Sets will guarantee at most 1 occurrence of each element, so it’s ideal to maintain a container with unique elements.

unique_numbers = {1, 2, 2, 2, 2, 2, 2, 3}
unique_numbers
{1, 2, 3}
unique_numbers.add(4)
unique_numbers
{1, 2, 3, 4}
unique_numbers.add(2)
unique_numbers
{1, 2, 3, 4}
Warning

Sets don’t preserve insertion order!

{"hello", "world", 1,2, "ciao", 4}
{1, 2, 4, 'ciao', 'hello', 'world'}

Sets are also much faster for lookups than lists and tuples. Let’s look at an example:

numbers_list = list(range(100_000_000))
numbers_tuple = tuple(range(100_000_000))
numbers_set = set(range(100_000_000))
%%time
500_000_000 in numbers_list
CPU times: user 739 ms, sys: 158 µs, total: 739 ms
Wall time: 735 ms
False
%%time
500_000_000 in numbers_tuple
CPU times: user 712 ms, sys: 0 ns, total: 712 ms
Wall time: 709 ms
False
%%time
500_000_000 in numbers_set
CPU times: user 3 µs, sys: 0 ns, total: 3 µs
Wall time: 7.63 µs
False

3.7 Dictionaries

A dictionary is a mapping from keys to values. They keys are unique. The values can be anything.

word2num = {
    "learning": 1,
    "python": 2,
    "is": 3,
    "fun": 4
}
word2num
{'learning': 1, 'python': 2, 'is': 3, 'fun': 4}

We can access them:

word2num["is"]
3
word2vec = {
    "python": [1,2,3],
    "tennis": [4,5,6]
}
word2vec
{'python': [1, 2, 3], 'tennis': [4, 5, 6]}
nested = {
    "first": {
        "one": 1,
        "two": 2,
    },
    "second": {
        "three": 3,
        "four": 4,
    },
}
nested
{'first': {'one': 1, 'two': 2}, 'second': {'three': 3, 'four': 4}}
nested["second"]["four"]
4

It’s important to keep in mind that dictionaries consist of items that are key-value pairs. That means we can build them from those paired items:

items = [
    ("one", 1),
    ("two", 2),
    ("three", 3),
]
items
[('one', 1), ('two', 2), ('three', 3)]
pairs = dict(items)
pairs, type(pairs)
({'one': 1, 'two': 2, 'three': 3}, dict)

We often want to iterate through the elements of a dictionary:

for key in pairs:
    print(key)
one
two
three
for key in pairs.keys():
    print(key)
one
two
three
for val in pairs.values():
    print(val)
1
2
3
for item in pairs.items():
    print(item)
('one', 1)
('two', 2)
('three', 3)
keys = pairs.keys()
keys
dict_keys(['one', 'two', 'three'])
vals = pairs.values()
vals
dict_values([1, 2, 3])

We can combine two collections, for example two lists, with the zip function. zip interleaves elements and stops as soon as the shortest collection is consumed:

dict(zip(keys, vals))
{'one': 1, 'two': 2, 'three': 3}

The inverse operation to zip can be done by unpacking (with *) the arguments like this:

list(zip(*zip(keys, vals)))
[('one', 'two', 'three'), (1, 2, 3)]

In Python we can “unpack” sequences directly during assignment:

seq = [1,2]
first, second = seq
first, second
(1, 2)
seq = (1, 2, 3, 4)
first, *rest = seq
first, rest
(1, [2, 3, 4])
first, *middle, last = seq
first, middle, last
(1, [2, 3], 4)

We can iterate and unpack the items of a dictionary on the fly:

for key, val in pairs.items():
    print(f"{key} -> {val}")
one -> 1
two -> 2
three -> 3

We can modify a dictionary, by updating an item or items:

pairs
{'one': 1, 'two': 2, 'three': 3}
pairs.update({"one": 111})
pairs
{'one': 111, 'two': 2, 'three': 3}
pairs["four"] = 4
pairs
{'one': 111, 'two': 2, 'three': 3, 'four': 4}

3.8 Exercises

  1. Create variables for the name of your favorite book and its author. Use an f-string to print a sentence that says, “My favorite book is [book] by [author].”
  2. Repeat it, but the book and author should be CAPITAL CASE.
  3. Repeat it, but the author and book should be each one a single word connected by hyphens, eg: “Charles Darwin” -> “charles-darwin”.
  4. Create a list of your 3 favorite books. Print the length of the list.
  5. Create a list of your 3 favorite sports.
  6. Concatenate the two previous lists and print the result.
  7. Print the last 2 elements of the concatenated list.
  8. Print the third element of the list with its characters in reversed order.
  9. Create a tuple containing 5 different cities. Print the first and last city from the tuple.
  10. Create a tuple of numbers and use slicing to print the middle three numbers.
  11. Create a dictionary with your favorite fruits as keys and their colors as values. Print the color of a specific fruit.
  12. Create a dictionary with countries as keys and their capitals as values. Print the keys and values separately.
  13. Invert the items of the dictionary, making the keys the values and vice versa.
  14. Create a set of your 3 favorite animals. Check if a specific animal is in the set.
  15. Create two sets of your favorite sports and find the difference between them.