Skip to article frontmatterSkip to article content

Python basics

This chapter will go over the basics of Python. Python supports many different programming styles from structured to object-oriented to functional programming. Perhaps the most important thing in Python is that everything is an object. This makes programming in some sense very easy but can lead to some weird results. This chapter will not into depth but cover enough to get you started. Other chapters will go more into depth about using Python for scientific computing.

Primatives

Primatives in Python include floats, integers, boolean, strings and None. We will go over the basics of these.

Integers

Integers are numbers that do not have values past the decimal place. In other languages you can have positive only integers and integers that can be negative and positive but in Python there is just one type of integer. Integers can be added, subtracted, multiplied and divided to create a new integer or inplace (using the += operator).

a = 1
b = 2
print(a+b, a-b, a*b, a/b, sep=", ")

# Inplace addition
print(a, id(a), sep=", ")
a += 3
print(a, id(a), sep=", ")
3, -1, 2, 0.5
1, 139672629036816
4, 139672629036912

Floats

Floats are numbers that have values past the decimal place. Floats can be added, subtracted, multiplied and divided to create a new integer or inplace.

a = 1.235
b = 2.355353
print(a+b, a-b, a*b, a/b, sep=", ")
3.5903530000000003, -1.120353, 2.908860955, 0.5243375409121266

Strings

Strings are essentially a list of characters or letters. You can add strings. You can turn integers and floats into strings by using the str function. When you add two strings together you create a new string.

a = "synapse"
b= "neuron"
c = str(1)
d = str(2.355)
e = a + b
print(e, a+c, e+d, sep=", ")
synapseneuron, synapse1, synapseneuron2.355

Boolean

Booleans are True or False. They can be created by testing for equality between two python objects such as numbers or strings using ==. You can also subtract add booleans to integers and floats. This is because booleans also evaluate to 0 and 1 (False and True respectively).

a = True
b = False
print(a == b, a-b, a-2.3, 2 == 2, "test" == "y")
False 1 -1.2999999999999998 True False

None

This may be somewhat controversial but Nones plays a very important role in Python especially for use in optional inputs to functions. While not traditionally counted as a primitive I believe that it is very fundamental to programming in Python.

a = None
print(a)
None

Containers

Containers are how you store many objects in Python. Typically containers include tuples, lists, dictionaries and sets. All containers in python can be indexed in some manner. Indexing just means grabbing an object by specifying its location in the container. Most indexing in Python uses the bracket [] operators.

Tuples, lists and sets can index using integers such as [1:3]. To the first object in a indexable container starts at 0, this is called zero indexing. The last object can is at index position lenght of container - 1. You can grab a subset by [1:3] and when you grab a subset you all the elements starting a 1 and up but not including 3. This can be confusing for people who have never coded so I recommend trying it out.

Dictionaries can be indexed using almost any Python object that is hashable or immutable. This includes integers, floats (don’t use this), tuples (very powerful) and strings.

Tuple

Tuples are an immutable container. Immutable means that you can not add elements to the tuple without creating a new tuple. Tuple can hold any type of primitive or other containers. They are also used implicitly when you return many items from a function which we will see later. Because tuples are immutable you cannot change the items in a tuple by object assignment. Note the us of id to show the Python object id changes when the tuple is modified.

j = (1, 3, "string", True)
print(j, id(j))
j += (1, )
print(j, id(j))

# Cannot do this
# j[0] = 3

# This won't work either
m = j[0]
m += 5
print(j)

(1, 3, 'string', True) 139672480426368
(1, 3, 'string', True, 1) 139672570330336
(1, 3, 'string', True, 1)

Lists

Lists are mutable containers. Mutable means you can modify the object by adding and subtracting elements or subsetting elements. One thing to note about lists is that the elements inside can be modified by assigment. You can create lists using brackets [] or if you have another container you can use the list() function to convert it to a list. You can append to list by using the .append() append and extend the list with another list or tuple by using the .extend() method or += operator.

j = [1, 3, "string", True]
print(j, id(j))
j += [1]
print(j, id(j))
j.append(1)
print(j, id(j))
j.extend([1])
j[0] = 3
j[1] += 2
print(j, id(j))
print(j[0], j[5], sep=", ")
[1, 3, 'string', True] 139672478684288
[1, 3, 'string', True, 1] 139672478684288
[1, 3, 'string', True, 1, 1] 139672478684288
[3, 5, 'string', True, 1, 1, 1] 139672478684288
3, 1

Dictionaries

Dictionaries are a key, value container. The key is hashed to a unique (most of the time) value and that value is used to store an object in an array. The object can be retrieved by using the key. Similar to lists dictionaries are mutable. Dictionaries can be created using the {} brackets or by the dict() function. You can add elements dictionaries using [], however if there is already an element with the same key you will overwrite the value. You can remove elements using the .pop() method.

a = {"b": 1, "c": 5}
b = dict(a=1)
print(a, b, sep=", ")
a.update(b)
a["testing"] = 3
a[0] = "testing"
b = a.pop("b")
print(a, b, sep=", ")
a["testing"] = "fire"
print(a, sep=", ")
{'b': 1, 'c': 5}, {'a': 1}
{'c': 5, 'a': 1, 'testing': 3, 0: 'testing'}, 1
{'c': 5, 'a': 1, 'testing': 'fire', 0: 'testing'}

Sets

Sets are similar to dictionary except they don’t have values, just keys. They cannot contain repeated elements. Sets are very useful to get unique values out of a list. Sets are mutable, so you update them with a list, tuple, set or string. If you want to add string to a set you will need to put it in another container since strings are just a container of characters (this can be confusing).

a = {"a", "b"}
print(a)
b = [1, 2, 2, 3, 4,4,4]
c = set(b)
print(b, c, sep=", ")
c.update([5])
c.update("string")
print(c)
{'a', 'b'}
[1, 2, 2, 3, 4, 4, 4], {1, 2, 3, 4}
{'g', 1, 2, 3, 4, 5, 't', 'i', 'n', 'r', 's'}

Operators

Operators essentially tell Python do do something on variables. There are many operators we will cover the most relevant.

Arithmetic operators

You may have noticed the use of mathematical operators including +, -, /, *. These are both basic mathematical functions and can act on other Python objects such as strings and other custom objects. Other mathematical functions include the inplace modifiers +=, -=, /=, *=. These will not return a new integer or float when used. There are two other useful operators the floor division // and the modulus %. These operators divide like you likely taught to divide. The floor gives the integer number of times a number goes into another and the modulus gives the remainder.

a = 5
b = 2
print(a//b, a%b)
a += 1
print(a)
2 1
6

Comparison operators

There are comparision operators greater than >, less than <, greater than or equal to >=, less than or equal to <=, equal to == and not equal to !=. Comparison operators always return a boolean value unless you try to compare an object that cannot be compared.

print(1>2, 2<3, 2!=3, sep=", ")
False, True, True

Functions

Functions are key to compartmentalizing you code. Functions can have any number of arguments and output a single value or a tuple of values. Functions are created using the syntax def function_name(args...):. Everything inside a function needs to be 4 whites spaces in. Most code editors allow you to use tab and insert the spaces for you. You can have positional, keyword, *args and **kwargs. Positional keywords mean you can just pass a value to a function without specifying which argument it is, the input will automatically be assigned to the arguments in order they occur in the function definition. Keywords can be supplied in any order but you must specify which argument they go to. Default (or optional) arguments are defined in the function definition (such b and c) where as a is necessary. *args are positional arguments not specified in the function definition but can be accessed in the fuction as a tuple. **kwargs are keyword arguments that are not specified in the function definition but can be accessed in the function as a dictionary. *args and **kwargs can be confusing, if anything I recommend sticking with **kwargs in the beginning. You will notice below that you can return all the items from a function into a single variable or explode the list into three variables. You can pass anything to a function in Python including other functions such as the built-in list().

def synapse(a, b=3, c=None, *args, **kwargs):
    print(a, b, c, args, kwargs, sep=", ")
    return a, b, c

output = synapse(3)
# The first three inputs are assigned to the function arguments everything else goes to *args
a, b, c = synapse(3, 12, c=list, k=11, mm=5)
print("Output 1:", output)
print("Output 2:", a, b, c)
3, 3, None, (), {}
3, 12, <class 'list'>, (), {'k': 11, 'mm': 5}
Output 1: (3, 3, None)
Output 2: 3 12 <class 'list'>

Scope

Scope is very important in Python when using functions. You can access variables outside a Python function that are not declared inside a Python function. This can cause a lot of problems. Python functions first look for the variable inside the function, if not found then outside the function. You will also notice that the function below does not return anything. Functions do not need to return anything but generally do.

b = 10
def test():
    print(b)
test()
10

Mutables, immutables, scope and pass by assignment

Modifications to mutable containers inside functions turn up outside functions. Notice how a gets modified and the returned value from test is the same object as a. However, primitives, even if you use the inplace operators, do not get modified in place.

a = [0,1,2]
c = 5
print(a, id(a), sep=", ")
def test(b, d):
    a = b
    b.append(5)
    d += 1
    return b, d, a
j, g, x = test(a, c)

print("Modifying lists:")
print(a, id(a), sep=", ")
print(j, id(j), sep=", ")
print(x, id(x), sep=", ")
print("Modifying primitives:")
print(c, id(c), sep=", ")
print(g, id(g), sep=", ")
[0, 1, 2], 139672480729472
Modifying lists:
[0, 1, 2, 5], 139672480729472
[0, 1, 2, 5], 139672480729472
[0, 1, 2, 5], 139672480729472
Modifying primitives:
5, 139672629036944
6, 139672629036976

The reason the mutable containers get modified in place even if you assign it to a new variable is pass by assignment that Python uses. In Python all objects are created into a memory pool. There is a reference counter that keeps track of how many assignments each object has. A single object can have multiple assignments. This is cuts on down on memory usage and helps with garbage collection (removal items that done have any assigments) but means that assigning a mutable object to a new variable does not create an new copy of the mutable object.

Flow control

Flow control includes if statements, for loops and while loops. There are other more complicated flow controls but we will not cover those here.

If statements

If statements control whether code precedes by using a comparison operator or if an object is a specific type. The key is that the statement must resolve to a boolean value. Typically you start with a if statement then procede to a elif statement if need and finally end with an else statement. You can also just use an if statement.

a = None
b = 3
print("First statement")
if a:
    print(a)
print("Second statement")
if b: 
    print(b)
print("Third statement")
if a:
    print(a)
elif isinstance(b, int):
    print(b)

print("Fourth statement")
if a:
    print(a)
else:
    print(a)
First statement
Second statement
3
Third statement
3
Fourth statement
None

For loops

For loops allow you to loop through an iterable. Iterables include lists, tuples, sets, and dictionaries. you start with a for statement. One common function is to use the range() function to use a for loop.

print("First loop")
for i in range(4):
    print(i)
print("Second loop")
j = [1,2,3,4]
for i in j:
    print(i)

print("Third loop")
j = {"a":1,"b":2,"c":3,"d":4}
for key, value in j.items():
    print(key, value)
First loop
0
1
2
3
Second loop
1
2
3
4
Third loop
a 1
b 2
c 3
d 4

While loops

While test for something on each iteration. They are useful is you don’t know how many loops you need to run. These are common in optimization problems.

i = 0
while i < 5:
    i += 1
    print(i) 
1
2
3
4
5

That is it for the basic Python tutorial. For further reading and learning I have some recommendations Books (in order):

  • Python for data analysis: data wrangling with pandas, NumPy, and Jupyter (now free online) McKinney, 2022

  • Fluent Python: clear, concise, and effective programming Ramalho, 2022

  • Python crash course: a hands-on, project-based introduction to programming Matthes, 2016

Websites:

References
  1. McKinney, W. (2022). Python for data analysis: data wrangling with pandas, NumPy, and Jupyter (Third edition). O’Reilly.
  2. Ramalho, L. (2022). Fluent Python: clear, concise, and effective programming (2nd ed). O’Reilly Media, Incorporated.
  3. Matthes, E. (2016). Python crash course: a hands-on, project-based introduction to programming. No Starch Press.