Open In Colab

Python Basics

This tutorial was inspired by and adapted from Shawn A. Rhoads’ PSYC 347 Course [CC BY-SA 4.0 License], Jeremy Manning’s PSYC 32 Course [MIT License], Whirlwind Tour of Python [CC0 1.0 License], DartBrains [CC BY-SA 4.0 License] and the NumPy Tutorials [BSD-3-Clause License].

Learning objectives

This notebook is intended to teach you basic python syntax for:

  1. Variables

  2. Operators

  3. If-statements

  4. Data types

  5. Functions

  6. For loops

  7. While loops

  8. Packages

Overview

This notebook provides a very high level overview of what it means to “write a computer program,” and some specific examples of how to write a program in Python (using Jupyter notebooks).

Note: this tutorial is intended to introduce you to the basics– not to provide a complete, or even fully self-contained, overview of Python programming.

A note on how to approach learning to code

(I really liked Jeremy’s advice on this, so I’m including it here)

Like any new skill, learning to code can be daunting. If you’ve never coded or taken courses on logic before, you might be about to embark on a journey towards an entirely new way of thinking and approaching problem solving. I’ve found four general principles to be useful:

  1. The first time you go through something, don’t expect to understand everything (or even anything) perfectly. Sometimes going through a new lesson or example might seem like encountering a foreign language (hint: maybe because it is!). Give yourself permission to relax and to not understand. Not “getting it” the first time doesn’t reflect on your intelligence or potential.

  2. If you’re feeling lost, it’s important to notice your points of confusion (even if you don’t immediately correct or resolve them). One of the most powerful ways of approaching coding is to break highly complex tasks down into successively simpler sub-tasks. You don’t always need to understand the full scope of the problem you’re working on– you just need to understand enough to break off a tiny piece of the next step in the puzzle. By the same token, approach learning as a problem solving journey. Try to take stock of what you’re not understanding, and carefully separate the material into “things you know (sufficiently well)” and “things you don’t know”. Use what you know to help further sub-divide the unknowns. Your goal should be to work towards an ability to precisely formulate questions. That will help you resolve your understanding (either by searching for the answer online or by asking others for help). Particularly when you’re starting out, it’s also OK not to know how to break your questions down into smaller chunks. You’ll pick it up as you go.

  3. The best predictor of successfully learning to code is persistence. Simply allowing yourself to be exposed to code (by reading and writing it, and by going through tutorials, etc.) will improve your ability to understand, even if those improvements aren’t immediately obvious or noticeable to you. One way to view the learning process is as a way of building up your fluency (through repeated exposure) in addition to adding the basic building blocks and vocabulary to your skill set. Even if you’re not adding new building blocks, simply gaining more exposure will improve your fluency. Over time this will make it easier for you to add new building blocks. Each new step you take in the learning process will allow you to gain a deeper understanding of previously learned material, and will help you to learn new material that much faster.

  4. The best way to learn to code is to write code. When you’re reading through a new tutorial or about a new idea, keep a scratch notebook open and code as you learn (control + alt + n or control + option + n). And once you’ve learned about something new, try to apply it to a question or problem you’re excited about. The potential applications of any given idea are nearly limitless. Allow yourself the freedom to get creative, make mistakes, break things, and try out new stuff!

What does it mean to ‘code’?

Coding, or computer programming, is the process of developing a set of instructions for a computer to run, typically with the goal of accomplishing some task (e.g. carrying out one or more calculations).

In this course we’ll be writing code in Python. Python is considered a high-level computer language. This means that, as the programmer, you can often ignore the particulars of how specifically your inputted instructions are carried out by the computer. Rather, those particulars are abstracted away. Instead, you’ll be describing at a “higher” (less detailed, more goal-oriented) level. By contrast, lower-level languages such as C require programmers to consider how different quantities are stored in the computer’s memory, and even lower-level languages such as assembly code require programmers to make use of the specific set of instructions available on the CPU.

How to think about coding

In Python, as with most computer languages, there are only a few types of commands and instructions that you need to learn about. In fact, you’ll learn about most of them in this notebook. Compared with human languages, which typically have many words and complex grammars, computer languages are relatively simple (with respect to their vocabularies and grammars). What makes Python (and other computer languages) so powerful is compositionality: the idea that you can build increasingly complicated functionality out of simpler building blocks. Everything computers do– your operating system, the Internet, self-driving cars, computing the millionth digit of pi, etc.– are all reducible to the same basic sets of instructions. Just like how human language words may be recombined and built-upon to communicate an infinite variety of ideas, the elements of computer languages may be recombined in similarly flexible and extensible ways. Whereas the goal of human language is to communicate ideas to other humans, the goal of computer language is to describe a set of calculations for the CPU to carry out in order to accomplish one or more tasks.

Once you learn the basic set of instructions, the entire “trick” of coding is to figure out how to string them together into meaningful code that carries out the desired tasks. Analogous to how there are many ways of “saying the same thing” in human languages, there are often many ways of writing code that carries out the same set of computations. And just like there are more (or less) efficient ways of conveying information to other people using human language, there are often more (or less) efficient ways of composing computer instructions.

In the remainder of this notebook you’ll learn about the major “words” and “building blocks” of Python programs.

Using Python like a calculator

In the Introduction and Overview notebook, we conceptualized thinking about Python like a calculator. For example, you can type in an equation, and when you “run” that line of code (shift + return) the answer is printed to the console window. Explore which operators (addition, subtraction, etc.) are supported and see if you can find some that aren’t! Also try to get a sense of the order of operations that determines which quantities are grouped and what order they are evaluated in.

3 * 5
15
(6 / 3 + 1) * (4 * 2 - 3)
15.0

Scripting

Python is a scripting language. This means that you could get the same functionality from a Python program by typing and running each line of the program in sequence, just like you might carry out a series of calculations by hand on a traditional calculator. (By contrast, compiled languages do not have this property.)

It can be useful to think of Python code as a script (a set of commands that are executed in sequence, from top to bottom) when you’re designing a program. Your job is to come up with the set of calculations that, when carried out in sequence, accomplish your desired goal.

Variables

A variable is a named object– i.e. a thing that Python knows has a particular value. It’s often useful to write code that incorporates named variables wherever possible (rather than hard-coding in specific numerical values). This way of abstracting away the specific values from the set of operations you want to perform on those values allows you to use the same line of code to perform different functions.

To define a variable, you use the assignment operator, =. The name of your variable goes on the left side of the assignment operator, and the value you want to assign to that variable goes on the right side. Play around with the example below to see how it works. For example, change the values of x and y and see how the answers change.

x = 3
y = 4
x + y
7

Types of Variables

  • Numeric types:

    • int, float, long, complex

  • string

  • boolean

    • True / False

Python’s simple types are summarized in the following table:

Python Scalar Types

Type

Example

Description

str

x = 'abc'

String: characters or text

int

x = 1

integers (i.e., whole numbers)

float

x = 1.0

floating-point numbers (i.e., real numbers)

complex

x = 1 + 2j

Complex numbers (i.e., numbers with real and imaginary part)

bool

x = True

Boolean: True/False values

NoneType

x = None

Special object indicating nulls

Use the type() function to find the type for a value or variable

# String
c = 'hello'
print(type(c))
<class 'str'>
# Integer
a = 1
print(type(a))
<class 'int'>
# Float
b = 1.0
print(type(b))
<class 'float'>
# Boolean
d = True
print(type(d))
<class 'bool'>
# None
e = None
print(type(e))
<class 'NoneType'>
# Cast integer to string
print(type(str(a)))
<class 'str'>

What are variables used for?

One way to think about variables is like the memory function on a traditional calculator. It gives you a way of storing some value that might be useful later on. By naming that stored quantity you (as the programmer) can keep track of what that quantity means and how it’s going to be used later on in your program.

Operators

In addition to the assignment operator (=), you have already been using several other operators in the code above:

  • the addition operator (+, as in a + b)

  • the multiplication operator (*, as in a * b)

There are a number of other useful operators:

  • the exponent operator (**, as in a ** b) raises the value of the first thing (a) to the power of the second thing (b)

  • the subtraction operator (-, as in a - b)

  • the division operator (/, as in a / b)

  • the modulus operator (%, as in a % b) computes the remainder when the first number is divided by the second number

  • the floor division operator (//, as in a // b) divides the first number by the second number and then removes any fractional parts of the result

  • the negation operator (-, as in -a) placed before a single number or variable multipies its value by -1

  • the unary plus operator (+, as in +a) placed before a single number or variable multiplies its value by 1. This is rarely used.

    • Some Python functions treat +a and a differently. One use of the unary operator is to provide a variable with the same value but that is distinguishable in certain use cases.

  • the add and operator (+=, as in a += b) is shorthand for a = a + b

  • the subtract and operator (-=, as in a -= b) is shorthand for a = a - b

  • the multiply and operator (*=, as in a *= b) is shorthand for a = a * b

  • the exponent and operator (**=, as in a **= b) is shorthand for a = a ** b

  • the modulus and operator (%=, as in a %= b) is shorthand for a = a % b

  • the floor division and operator (//=, as in a //= b) is shorthand for a = a // b

# Addition
a = 2 + 7
print(f'a = {a}')
a = 9
# Subtraction
b = a - 5
print(f'b = a-5 = {a}-5 = {b}')
b = a-5 = 9-5 = 4
# Multiplication
print(f'b*2 = {b}*2 = {b*2}')
b*2 = 4*2 = 8
# Division
print(f'a/3 = {a}/3 = {a/3}')
a/3 = 9/3 = 3.0
# Exponentiation
print(f'b^2 = b**2 = {b}**2 = {b**2}')
b^2 = b**2 = 4**2 = 16
# Modulo 
print('(Modulo returns the remainder of dividing the left hand operand by right hand operand)')
print(f'remainder of b/9 = remainder of {b}/9 = {b%9}')
(Modulo returns the remainder of dividing the left hand operand by right hand operand)
remainder of b/9 = remainder of 4/9 = 4

Logical operators

We will find that it is often useful to know if a particular set of circumstances is true at a particular point in the course of running our computer program. For example, maybe we would want to run a particular set of calculations in one scenario but a different set of calculations in another scenario. Logical operators are operators that work only on Boolean variables– i.e. variables that take on special values, True (or 1) and False (or 0). Logical operators always yield either True or False:

  • or: a or b is True if either a is True or if b is True, and False otherwise.

  • and: a and b is True if both a is True and b is True, and is False otherwise.

  • not: not a is True if a is False, and is False if a is True.

Logical operators play a very important role in computer programs. One common use of logical operators is in determining whether (or when) a particular computer instruction will run. Logical operators may also be combined to define complex tests. For example, you might want to know if either a or b (but not c) are True:

d = (a or b) and (not c)

Equality operator

Sometimes we want to know whether a particular variable has a specific value. To do this, we use the equality operator, ==:

  • a == b is True if a has the same value as b, and is False otherwise. One common use of the equality operator is in determining whether a particular condition has been satisfied. For example, you might want to execute an instruction 10 times, with a counter (i) that keeps track of how many times the instruction has been executed. You could include a check of whether i == 10 to determine whether the program should execute the instruction again or not.

As a shorthand for not (a == b), Python also provides an inequality operator, !=. The statement a != b is True if a does not have the same value as b. The <> operator (as in a <> b) is similar to !=, but is rarely used in practice.

Other comparison operators

Python also includes operators that allow comparisons of different values:

  • a > b is True if a is strictly greater than b and False otherwise

  • a >= b is True if a is greater than or equal to b and False otherwise

  • a < b is True if a is strictly less than b and False otherwise

  • a <= b is True if a is less than or equal to b and False otherwise

# Works for strings
a = 'hello'
b = 'world'
c = 'Hello'
print('a==b: ' + str(a==b))
print('a==c: ' + str(a==c))
print('a!=b: ' + str(a!=b))
print()
a==b: False
a==c: False
a!=b: True
# Works for numeric
d = 5
e = 8
print('d < e: ' + str(d < e))
print('d >= e: ' + str(d >= e))
d < e: True
d >= e: False

String Operators

Some of the arithmetic operators also have meaning for strings. E.g. for string concatenation use + sign

String repetition: Use * sign with a number of repetitions

# Assigning and combining strings
a = 'Hello'
b = 'World'
print(a + ' ' + b)
Hello World
# f-strings
firstname = 'Shawn'
print(f'My name is {firstname}')
My name is Shawn
# Combining f-strings
firstname = 'Shawn'
lastname = 'Rhoads'

print(f'My name is {firstname} {lastname}')
My name is Shawn Rhoads
# Repeat String without spaces
print(str(a)*5)
HelloHelloHelloHelloHello
# Repeat String with spaces
print(str(a+' ')*5)
Hello Hello Hello Hello Hello 

What are operators used for?

Operators are how you get different quantities to “interact” so that you can compute with them. For example, the addition operator takes two quantities (variables), adds them together, and gives you the resulting sum as a new (single) quantity. You can assign this sum to a new variable so that you can use or refer to it later:

c = a + b

In this way, operators are the “verbs” of computer languages.

Lists

In Python, a list is a mutable sequence of values. Mutable means that we can change separate entries within a list.

  • Each value in the list is an element or item

  • Elements can be any Python data type

  • Lists can mix data types

  • Lists are initialized with [] or list()

list_a = [1,2,3]
print(list_a)
[1, 2, 3]
list_of_strings = ['hello', 'bye', 'hi']
print(list_of_strings)
['hello', 'bye', 'hi']

Elements can be nested lists

nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(nested)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Indexing in Python

  • Indexing in Python allows us to access individual elements within a sequence, such as a string, list, or tuple.

  • Indexing starts from 0, which means the first element is at index 0, the second element at index 1, and so on.

  • To access an element at a specific index, you can use square brackets [] after the sequence name, followed by the index number. For example, my_list[2] will give you the element at index 2 in the list.

  • You can also use negative indices to access elements from the end of the sequence. For instance, my_string[-1] will give you the last character of the string.

  • You can even use a range of indices to access multiple elements at once. For example, my_list[1:4] will give you elements from index 1 up to (but not including) index 4 in the list.

  • Remember that when using a range of indices, the start index is inclusive, while the end index is exclusive.

By understanding indexing, you’ll be able to retrieve specific elements from a sequence, manipulate data, and perform various operations in Python!

# Indexing and Slicing
list_b = ['list','of','things']
print(list_b[0])
print(list_b[1:3])
list
['of', 'things']

Elements within a list are indexed (starting with 0)

list_b[0]
'list'

Lists can be sliced like this:

  • l[start:stop:stride]

list_to_slice = [1,2,3,4,5,4,3,2,1]
list_to_slice[0::2]
[1, 3, 5, 3, 1]
list_to_slice[0:-1:2]
[1, 3, 5, 3]

Lists have many useful methods. For example:

  • append() adds an element to the end of a list

  • insert() adds an element at a specified index

  • pop() removes and returns the last element of a list

  • remove() removes the first matching element from a list

  • sort() sorts a list in place

# append
list_b.append('new')
list_b.append('thing')
print(list_b)
['list', 'of', 'things', 'new', 'thing']
# insert
list_b.insert(2,'python')
print(list_b)
['list', 'of', 'python', 'things', 'new', 'thing']
# pop
list_b.pop()
'thing'
# remove
list_b.remove('list')
print(list_b)
['of', 'python', 'things', 'new']
# sort
list_b.sort()
print(list_b)
['new', 'of', 'python', 'things']
# Methods could be applied to other objects (like strings)
example_string = 'Summer Program in Computational Psychiatry Education'
print(example_string)
print(example_string.upper())
Summer Program in Computational Psychiatry Education
SUMMER PROGRAM IN COMPUTATIONAL PSYCHIATRY EDUCATION

Comments and print statements

Sometimes it’s useful to write little notes to yourself (and other people who might want to read your code) to keep track of what the different parts of your code do. There are two ways to add notes to your code:

  • You can create an “inline comment” by adding a ‘#’ followed by the text of your comment.

  • You can create a “block comment” by adding a set of triple quotes, followed by one or more lines of text, and then followed by a second set of of triple quotes. You can use single quotes (‘) or double quotes (“) to denote a block comment, as long as you start and end your block comment with the same type of quote.

The Python interpreter (i.e. the computing engine that turns your computer code into executed instructions) ignores all comments when it is running your code.

You can also tell the interpreter to print out the value of something by using the print function, as illustrated below.

'''
This block of text is a block comment, enclosed in triple quotes.  The comment
can span multiple lines. Anything written inside of a comment will be ignored by
the computer when the program runs.
'''

pi = 3.14159265359 #ratio of a circle's circumference to its diameter

r1 = 6 #radius of circle 1
r2 = 8 #radius of circle 2

#the ** operator means "raise the value on the left to the power of the value of
#the thing on the right".  Notice how order of operations comes into effect.
area1 = pi * r1 ** 2
area2 = pi * r2 ** 2

print(area1)
print(area2)
113.09733552924
201.06192982976

if statements

Python contains a number of keywords that allow you to control the flow of instructions that the computer executes. One of the main keywords is the if statement. It runs one or more lines of code only if the quantity being evaluated is True:

x = 3
if x == 3: #notice the colon
    print('Run this line') #all lines in the body of the if statement are indented
Run this line

elif and else statements

Whereas the body (indented part) of an if statement will simply be skipped if the evaluated function passed to an if statement is False, you can also specify what to do under other possible circumstances:

  • The elif statement comes right after an if statement. It allows you to specify an alternative set of conditions. You can use multiple elif statements in sequence; once any of them evaluate to True the body of that statement is run and the sequence is aborted (no other elif statements are tested).

  • The else keyword comes after an if statement and (optionally) one or more elif statments. The body of an else statement runs only if none of the preceeding if or elif statements ran.

my_name = 'Shawn Rhoads'
if my_name == 'Shawn Rhoads':
    print('You are the course instructor.')
    print('You are also a Postdoc.')
elif my_name == 'Xiaosi Gu':
    print('You are the current Director of the Center for Computational Psychiatry.')
elif my_name == 'Marie Curie':
    print('You won two Nobel Prizes.')
else:
    print('I don\'t know you-- nice to meet you!') #note the "escape character" before the single quote
You are the course instructor.
You are also a Postdoc.

What are if, elif, and else statements used for?

These statements help to control the flow of a program. Specifically, they allow you to specify the circumstances under which a particular instruction will be run (or skipped). In the preceeding example, the value of the my_name variable will determine which statement gets printed out. The other statements are skipped over. Try experimenting! For example, in the previous cell you might try:

  • changing the value of my_name

  • adding or removing instructions within the body of the if, elif, or else statements

  • adding additional elif conditions

What do you think would happen if you replaced some of the elif statements with if statements? Can you figure out what’s different about how the program runs?

Datatypes

The computational elements that Python “computes with” can take on different types of values, called datatypes. You’ve gotten to see a few different datatypes in above examples; here are some of the datatypes you’ll likely encounter frequently:

  • Integers (int): non-decimal scalar values (e.g., -50, 326, 0, 2500, etc.)

  • Floating points numbers (float): Real-valued scalars (e.g., 1.2345, -10.923, 0.01, 2.0, etc.).

  • Boolean (bool): True or False.

  • Strings (str): sequences of characters or symbols, enclosed in single or double quotes (e.g., 'hello', 'This is a single quoted string!', "This is a double quoted string...", etc.).

  • Lists (list): an ordered set of objects (each element of the set can be of any datatype, including a list!). Example; ['apple', 7, '3', [6, 'twenty five', -65.4321], 7]

  • Dictionary (dict): an unordered set of named properties that each has an assigned value (each value can be of any datatype, including a Dictionary!). Example: {'name': 'Introduction to Programming for Psychological Scientists', 'number': 32, 'department': 'PSYC', 'url': 'https://github.com/ContextLab/cs-for-psych'}.

  • Null value (None): a special datatype that doesn’t have any specified value. Useful as a “default” value, e.g. before you have enough information to compute the answer.

Typecasting

Many datatypes may be converted to other datatypes using typecasting. For example, float(3) converts the integer 3 into a floating point decimal. In the next cell, explore what happens when you try to convert between different datatypes. You can use the type function to ask Python what the datatype of a given entity is. For example, calling print on the output of type will display the datatype of whatever you pass to type.

print(type(3))
list(str(float(int(3.4)))) #can you figure out what's happening here?
<class 'int'>
['3', '.', '0']

Once you figure out what’s happening in the line above, try to make a prediction: what would the following line return?

list(str(float(int(3.9))))

Now test your prediction. What do you think is happening?

What are datatypes used for?

The computer executing your code needs to know how to handle the values you input and the answers you “get out” of the different computations in your program. Although Python will try to execute instructions without considering the datatypes of the variables, your code will crash (fail to run and output an error message) if Python can’t figure out how to apply the given operators to the given data types. As a programmer, it is often useful to consider what datatypes you are expecting different variables to take on at different points in your program. For example:

x = 'This is a test'

if type(x) == str:
    print('x is a string')
elif type(x) == int:
    print('x is an integer')
else:
    print("I'm not sure how to handle x!") #note use of double quotes allows us to have a single quote in the printed string (without an escape character)!
x is a string

Functions

We’ve already come across several functions, such as print, type, and various operators (e.g. +, -, *, /, **, %, etc.– operators are a special type of function.). A function is a special datatype that takes in zero or more arguments (i.e. inputs) and produces zero or more actions or outputs.

Sometimes a function is written so that its main purpose is to carry out some action, such as saving a file or generating a figure. In other cases, the purpose of a function is to carry out some computation on the input arguments. The syntax for defining a function is:

def <function_name>(arg1, arg2, ..., argN):
  <instruction 1>
  <instruction 2>
  ...
  <instruction M>
  return <value>

Here the def command tells Python that we’ll be defining a new function called <function_name>. The function will take in a list of arguments (arg1, arg2, arg3, etc.), and carry out some operations. Finally, the return command tells Python what the value of the function itself is, given the inputs that were passed to it. If the return line is ommitted, then the function will evaluate to None by default. (Note: the body of your function definition needs to contain at least one line of code, or the interpreter will throw an error.) The next cell contains an example function definition.

Note that function definitions may be nested– in other words, the instructions specified in the body of a function definition may themselves be function definitions!

def square(x):
    return x**2

print(square(1))
print(square(2))
print(square(3))
1
4
9

What are functions used for?

Functions are the computational construct that allows you to compose sequences of instructions together in increasingly complex ways. For example, suppose you wanted to implement the power operator (**) yourself, only knowing about the multiplication (*) operator. You could do something like this:

def power(x, n):
    '''
    raise x to the nth power
    '''
    if not (type(n) == int):
        raise(Exception("I don't know how to handle non-integer powers")) #prints out an error message!

  #why are each of these control flow statements needed?
    if n == 0:
        return 1
    elif n < 0:
        return 1 / power(x, -n)
    else:
        return x * power(x, n - 1) #this line is the main "workhorse" of the function-- why?

#test out a few examples
print(power(3, 2))
print(power(5, 3))
print(power(10, -6))
print(power(4, 1.5)) #should give an error.  What happens if you run this line before the other print statements?
9
125
1e-06
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
/tmp/ipykernel_2295/1985846777.py in <module>
     18 print(power(5, 3))
     19 print(power(10, -6))
---> 20 print(power(4, 1.5)) #should give an error.  What happens if you run this line before the other print statements?

/tmp/ipykernel_2295/1985846777.py in power(x, n)
      4     '''
      5     if not (type(n) == int):
----> 6         raise(Exception("I don't know how to handle non-integer powers")) #prints out an error message!
      7 
      8   #why are each of these control flow statements needed?

Exception: I don't know how to handle non-integer powers

Notice how the power function calls itself! Function definitions that include self-references are an example of a techinique called recursion. Creating a recursive function allowed us to write “simpler” code.

Despite that recursive functions may have few lines, they can nonetheless be tricky to understand. As an exercise, try going carefully through the power function above and attempting to understand the code in detail. For example:

  • Can you follow how many times the power function is called in each of the above scenarios?

  • Can you follow how and why the power function “works” (i.e., why it returns the first argument raised to the power of the second argument)?

In exploring the above code, you may find it useful to use strategically placed print statements to display messages to indicate when different parts of the code are being reached and/or what the values of different variables are at different stages of the function’s execution.

Another example of compositionality in functions is given in the next cell:

def add_and_print(a, b):
    c = a + b
    print('The answer is: ', c)

add_and_print(3.2, 1.4)
add_and_print(-7, 12)

Here the add_and_print function includes calls to both the addition (+) operator and the print function. Combinining these into a single function allows us to execute the same sequence of commands with only a small amount of additional code (e.g., rather than re-writing the full set of instructions multiple times). In turn, the add_and_print function could be incorporated into one or more other functions that carried out additional tasks.

Loops

It’s often useful to carry out a similar operation many times. For example, you might want to read in each file in a folder and apply the same basic set of commands to each file’s contents. Loops provide a way of writing efficient and flexible code that involves doing an operation several times.

There are two types of loops in Python: for loops and while loops.

for loops

This type of loop carries out one or more operations on each element in a given list. The syntax is:

for i in <list of items>:
  <instruction 1>
  <instruction 2>
  ...
  <instruction N>

where i is, in turn, set to the value of each element of the given list, and the instructions defined in the body of the loop are carried out. (Here i is just an example variable name; in practice any variable name may be used as a stand-in for i.) In other words, the instructions in a for loop are carried out for each value in the given list.

for i in [0, 1, 2, 3, 4, 5]:
    print(i)
for x in ['a', 'b', 'c', 'd']:
    print(x)
for i in range(6):
    print(i)
for i in range(2,5):
    print(i)
# indexing using for loops
list_a = ['P','S','Y','C','H']

for index, value in enumerate(list_a):
    print(index, value)
example_string = "Python is making science easier"
for c in example_string:
    print(c)
# appending to lists in for loop
print(list_b)

print('\n...running for loop\n')
for value in ['a','b','c']:
    list_b.append(value)
    
print(list_b)
# manipulating lists using for loops
for value in ['m','o','d','e','l','s']:
    print(value.upper())
# only capitalize every other letter
for index, value in enumerate(['m','o','d','e','l','s']):
    if index % 2:
        print(value.upper())
    else:
        print(value)

As an alternative, list comprehension is a very powerful technique allowing for efficient construction of new lists.

[a for a in l]
# list comprehension
list_example = [10, 11, 12, 13, 14, 15]

print([x*9 for x in list_example])
# List Comprehension with methods
list_example2 = ['words', 'to', 'change']

print([x.upper() for x in list_example2])

while loops

This type of loop carries out one or more operations while the given logic statement holds true. The syntax is:

while <statement>:
  <instruction 1>
  <instruction 2>
  ...
  <instruction N>

where <statement> (i.e. the loop condition) is any Python expression that can be typecast to a bool. These types of loops are useful when the number of repetitions needed to carry out a particular task is not known in advance.

Infinite loops

It is important that the statement used to determine whether the while loop continues with another execution or terminates is modified within the body of the loop. In other words, the parameters of the condition that is being tested for should be adjusted each time the loop executes another cycle. If the loop condition never changes its value from True, the while loop will continue looping forever; this is called an infinite loop. Infinite loops will freeze your computer program until they are manually halted by pressing ctrl + c (or by selecting “Interrupt execution” from the Runtime menu above).

Nested loops

Both for and while loops may themselves contain other loops (of either type). For example, nested loops can be useful when you want to carry out some sequence of operations on each combination of a set of things.

i = 10
while i > 0:
    i = i - 1
    print(i)

What are loops used for?

Loops (like if, elif, and else statements) are the other major way of controling the flow of programs. To think about: under what circumstances might you want to use recursion (like the power example above) versus loops? For example, could you re-write the power function using a for or while loop?

Importing new functions

Python comes with a set of built-in functions. The dir() command lists the set of functions currently available to call. Starting out in this course you’ll be directly told which libraries you need to import and how to import them, and which functions within those libraries might be useful. As the course progresses and you become familiar with the different libraries that are commonly used, you’ll start to gain an intuition for which libraries to import and/or how to search for libraries that might provide functionality that’s missing from the set of built-in functions.

As an aside: if you dig deep enough, every Python library is built upon the same set of built-in functions that you have access to without importing any libraries. Therefore you could (in theory) accomplish exactly the same tasks without ever importing a library. In this way, Python’s notion of importing is really a convenience that improves efficiency by enabling you to more easily re-use other people’s (or your) code. Python’s library infrastructure also makes Python an attractive language for scientists in that code may be easily shared. (Later in the course we’ll discuss how to make code that you write available to the community as a shareable library.)

You can import new functions that other people have written and made available to the community using an import statement. Knowing which libraries (sets of functions) to import requires some Googling or checking in with other users. The syntax for importing a library is: import <library name>. For example import math will import Python’s Mathematics library, giving you access to new Math-related functions.

The from keyword

Sometimes it is useful to import only a subset of the total available functions from a library. For example, a particular function in a library may have the same name as a function that you want to write yourself. The from keyword allows you to select a specific set of functions from a given library using:

from <library name> import <function1>, <function2>, ..., <functionN>

You can also use the dir command on an imported library to determine which functions are available to import from that library.

The as keyword

Sometimes it is convenient to rename an imported library or function. For example, the library might have a long name that is cumbersome to type. The as keyword can be used to rename libraries, functions, or both. For example:

import math as m
from math import degrees as deg
import math as m
from math import degrees as deg

m.sin(m.pi) - m.cos(m.pi)

Weird stuff

Python represents all numbers in Base 2 (i.e. binary). Occassionally representing Base 10 numbers in Base 2 can lead to seemingly strange behaviors. One such example is shown in the next cell. To think about: how or when might this matter?

0.3 - 0.2 - 0.1

Answer: This output may seem weird because of how floating-point numbers are represented in computers. In base 10, the decimal number 0.3 can be represented exactly as 0.3. However, in base 2 (which is how floating-point numbers are represented in computers), the decimal number 0.3 cannot be represented exactly. Instead, it is represented as an infinitely repeating binary fraction.

When you subtract 0.2 from 0.3, you get 0.1, which can be represented exactly in both base 10 and base 2. However, when you subtract 0.1 from 0.2, you get a number that cannot be represented exactly in base 2. This can lead to small rounding errors that accumulate over time and cause unexpected results like the one you’re seeing.

Another “weird” property of Python (e.g., as compared with most other programming languages) is that whitespace (spaces and tabs) at the beginning of a line carry meaning. For example, the code block:

if a == b:
  a = 0
  b = 1

is different from

if a == b:
  a = 0
b = 1

Specifically, in the first code block (where b = 1 is indented), b will be assigned a value of 1 if and only if a is equal to b. This is because the lines with the same number of leading whitespace characters are treated as “grouped” (i.e., belonging to the same statement body).

In the second code block (where b = 1 is not indented), b is assigned a value of 1 regardless of whether a and b are equal. This is because the unindented b = 1 line is interpreted as falling outside of the body (scope) of the if statement.

Getting help

The help function may be called for any built-in (or imported) Python function. This will display a pop-up message with instructions describing how to use the given function. For example:

help(m.sin)

When you try to perform an invalid operation (e.g. using incorrect syntax, or using the wrong data type, etc.) your program will crash and print out an error message. These error messages can help point you to where the problem in your code was.

1 + 'test' #trying to add two data types for which the addition operation isn't supported

Although they are very useful, in practice the built-in help function and error messages are often insufficient for solving tricky problems. Some great places to get help are:

  • Python tutorials: if you feel you require an in-depth understanding to solve your problem, there are many free tutorials available on a wide array of topics. This one is a good place to start for basic questions.

  • Google: if you are trying to figure out an unfamiliar function, or if you encounter an error message that doesn’t make sense, try searching for someone else’s solution.

  • Q&A: come to my Q&A hours, or send me a message on Slack, and I’ll help get you unstuck.

Happy coding!

Programming is one of the most rewarding skills you can pick up. The modern world runs on computers, and learning to code well will provide you with an enormous advantage in a wide range of life venues. Have fun learning, don’t be afraid to make mistakes, and learn to ask for help when you need it!