print("You ran me!")Python fundamentals
1. Meet the Python Notebook
Welcome to the absolute basics tutorial for Python!
This file is called a Python/Jupyter Notebook: an interactive way to write Python code and prose together in a single file.
Notebooks are one of the most popular ways to use Python for data analysis and data-science. A notebook consists of separate cells that demarcate blocks of text or code. The two main types of cells you create are:
- Code cell: contains Python you want to execute
- Markdown cell: contains prose that you want to render (like this cell)
You can move between cells using the arrow keys on your keyboard and you can execute a cell by using the keyboard-shortcut shift+enter or ctrl+enter
Try selecting the next cell by pressing down and then execute it by pressing shift+enter. You should see some output generated below the cell.
To instead edit the contents of cell just press enter. You can press escape to exit editing mode.
Try that on the next cell by changing the text inside of quotes to something else and then execute it:
print("Change me!")You can make new cells above or below an existing one using the “+” button in the toolbar, searching in the command palette, or using a keyboard shortcut a for above, b for below` when you’re not editing a cell.
Try making a new cell below this one and then copy and paste the contents of the previous cell into it and execute it
Recap
- One of the primary ways to work with Python interactively is to use notebooks like this one’
- Notebooks contain code and prose cells and their execution outputs all in a single file
- We primary work with notebooks by
- Make a new cell
- Write Python (or markdown/text) inside of it
- Execute the cell to get the output
2. Python basics
Let’s cover some Python basics to help you get going fast.
For more detailed tutorials we highly recommend checking out the links at the bottom of this notebook!
Python is a modern interpreted programming language that is incredibly popular because it looks more like English that most other programming languages.
Just like English, in Python we write statements that can do a variety of things:
- create Variables which store different values
- create Functions which are little recipes to turn inputs into outputs
- create Logic that does different things depending on conditions we check
- create Loops to repeat bits of logic over-and-over
Let’s look at each of these in turn.
In the examples below, you’ll notice that some lines are prefixed with # and appear in a different color.
These are called code comments, which are ignored by Python and useful for providing short explanations inline.
You can also “comment-out code” which just means to prefix the like with # effectively disabling that line during execution.
2.1 Variables
Variables are names we give to values so we can remember and use them later.
# Assign a value to a variable
name = "penguin"
age = 5
weight = 3.2
# Just typing the name of the variable with print it's value out
name
age
weightYou can see that variables can be of different types depending on the values we want to store. The most common ones are:
- Numbers (integers or decimals/floats)
- Text (strings of letters)
- Boolean (true or false)
# Numbers
integer_number = 42 # whole numbers
decimal_number = 3.14 # decimal numbers (called 'float')# Text (strings)
species = "human" # use single or double quotes for text
letter = "H"# Boolean (True/False)
is_cute = True
can_fly = FalseImportant
In this note book we’re separating our code into different cells, but any variables we define are avaialble to all cells.
In other words, code-cells provide visual separation while we work, but to Python all code in the notebook is part of the same session.
# We defined species above but can use it anywhere!
print(species)Basic operations between variables
After we define variables we can use them in a variety of ways.
The simplest operations are just basic calculator-like-arithmetic:
# Math operations
a = 10
b = 3
a + b # addition
a - b # subtraction
a * b # multiplication
a / b # division
a**b # power (10 to the power of 3)
a % b # remainder (10 divided by 3 has remainder 1)What’s neat is that Python can also use these operations on Text (strings)!
# String operations
first_name = "Emperor"
last_name = "Penguin"
# Combine strings (concatenation)
full_name = first_name + " " + last_name
full_name# Repeat strings
cheer = "Go! " * 3
cheerTrying making a few cells below this one and playing with some basic operations
# Your code here# Your code here# Your code here2.2 Functions
Functions in Python are little recipes that we can reuse. Function do the following:
- take some variables as input (arguments; optional)
- perform some operations
- return new variables as output (return values; optional)
# Create a variable
word = "Psychology"
# Check it's length using the len() function
number_of_chars = len(word)
number_of_charsIn the example above, we passed the variable word to the function len and it returned a value counting the number of letters in the word.
But functions don’t have to return anything. Sometimes they are just useful for producing some effect like saving a file or printing something to the screen.
In fact that’s exactly what the print we saw at the beginning does!
text_to_print = "Hello World"
output = print(text_to_print)If we check the value of output we’ll see it’s empty because print doesn’t return anything.
It just produces an effect of displaying text.
outputCreating your own functions
You can make your own functions using the def keyword in the following way
def add(a, b):
return a + bHere we create a new function called add that accepts 2 arguments a and b and returns their sum
Then we can use it like any other function:
add(1, 2)Try creating you own function called greet that accepts a name as input and returns “Hello” and the name.
You can edit the function definition the cell below and then execute the cell to see if it works
# Edit me!
def greet():
pass
# Use the function
message = greet("Goofy")
print(message)Functions also don’t have to take any input. You can simple use them to encapsulate some code you want to run over and over again.
# define the function
def print_hello_goodbye():
print("hello")
print("goodbye")
# Run the function no inputs or outputs!
print_hello_goodbye()2.3 Logic
Python has special keywords that allow us to control our logic with conditional statements: if, elif, and else.
We indent our code to use them like this:
if condition:
do_something
elif other_condition:
do_something_else
else:
otherwise_do_this
Each keyword expects condition or other_condition to be a boolean variable, i.e. True or False.
We can create these using comparison operations between variables.
These are just like the basic operations (e.g. +, -), but instead compare values and return True/False
# Define variables
a = 10
b = 20
# Compare them
a < bComparison operations
These are the comparison operators you’ll use most often:
a == b # equals
a != b # not equals
a < b # less than
a <= b # less than or equal
a > b # greater than
a >= b # greater than or equalOne special comparison operator is is which you can use to compare to the special None type.
Remember how print doesn’t return anything? Let’s check using is
output = print("hello")
output is NoneLet’s see how to use these with the if/elif/else keywords.
Execute the cell below to see what it prints. Then try changing the name variable and rexecuting the cell to see what happens.
# Define a variable
name = "Eshin"
# Check it's value and do different stuff
if name == "Eshin":
print("Your role is assistant professor")
else:
print("I'm not sure what your role is...")Above we use == to compare the value of the variable name to the string "Eshin" and change what we do if it’s True vs False
We can also combine comparisons using and, or, not for more complex conditional statements.
Below, try playing with guest_name and guest_age and rerunning the cell to understand what’s happening:
guest_name = "Goofy"
guest_age = 18
if guest_age >= 21 and guest_name != "Pluto":
print("Welcome to Mickey's restaurant!")
else:
print("Sorry you can't enter")and, or, and not behave as your intuition might suggest: they combine multiple True/False statements into a single more complex statement
# Define some variables
a = 2
b = -1
a < 5
not a < 5 # negates the check
a < 5 and b > 0 # checks that BOTH are True
a < 5 or b > 0 # checks that EITHER is True
# We can use parentheses to group more complicated statements
(a < 5 and b > 0) or (a < 3)Here’s an example where we combine what learned about defining our own functions with some conditional logic:
# We're just defining the function in this cell so it doesn't return or print anything
# it just makes subtract_bigger() available to the rest of the cells in the notebook
def subtract_bigger(a, b):
"""Always subtracts the smaller number from the bigger number"""
if a > b:
return a - b
elif b > a:
return b - a# 4 - 3 = 1
subtract_bigger(4, 3)
# Same!
subtract_bigger(3, 4)In the next cell we’ve passed 2 numbers that are the same to subtract_bigger, but our function silently fails… Nothing is returned from the output.
Can you update the logic in the function to handle this? The function should return 0
Remember to re-execute the cell containing the function definition after editing it!
subtract_bigger(4, 4)Recap
Try using the next few cells to play the with concepts you’ve learned so far:
- defining variables
- using basic operations
- using comparison operations
- using functions
- creating functions
- using logic
# Your code here# Your code here# Your code here3. Beyond strings and numbers
Most often you’ll be using Python multiple variables at once, e.g. a list of participant IDs, or stimuli names.
Python has a few built-in types of “containers” that make it easy to store multiple variables together
3.1 Lists
Lists are the simplest, and most flexible way to multiple variables based on their order.
We create lists using square brackets []
# Create a list
species = ["Adelie", "Gentoo", "Chinstrap"]
# Look at it's value
species
# Use the len function to check it's length
len(species)To get specific value(s) from a list we index it using square brackets [] and the numerical position of the item we want.
Important
While it may not make much sense now, Python and many other programming languages start counting at 0 not at 1.
This is known as “zero-based-indexing” and actually turns out to be very convenient for many operations.
It will take a little getting used to, but just remember that the:
- first-item, 0th position
- second-item, 1st position
- third-item, 2nd position…
Let’s get the first item in the list:
species[0]Or the second item
species[1]Python also let’s using negative positions which counts backwards from the last item in reverse order
species[-1] # last itemThis is much easier that trying to get the last item of a list by first counting it’s length to figure out how many items it has, e.g.
species[len(species) - 1]We can also grab multiple values by slicing a list.
We provide a range of values we’d like and then get back a smaller list with those values.
We use the following format:
start:stop:step
start- where to begin
- can leave out if you want to start at beginning (
0th index)
stop- “up-to” this position
- not included in the result
- can leave out if you want to include “until the end”
step- optional, how to skip between items
- defaults to
1
Play with start, stop, and step below to get a feel for how they behave
names = ["Eshin", "Emma", "Justin", "Aaron", "Jacob", "Jinwoo"]
start = 0
stop = 2
step = 1
names[start:stop:step]Some common slicing patterns you’ll might look like:
# Get the first 3 items
# Leaving out start = start at 0
names[:3]# 3rd name until the end
names[2:]# Get the last 3 items
names[-3:]
# Take a moment to think/play around about how this works
# In this case:
# step = -3
# stop = len(names) because we left it out
# step = 1 because we left it out# Reverse the list
names[::-1]
# This works because we're stepping through the list backwards!
# start = 0
# stop = len(names)
# step = -1Methods
So far we’ve been playing with variables (e.g. names) and functions (e.g. len()) as separate “things” in Python.
However, many objects actually have special functions that come “pre-attached” to them letting them alter that object somehow.
We refer to these functions as an object’s methods and we refer to them using .methodname() syntax
Let’s say we want to add another item to our names list after creating it already. We can use the .append method to do just that!
# Before
names# Call method
names.append("New Lab Member")
# After
namesNotice that we using .append() just like any other function by passing in 1 or more arguments.
Unlike a regular function, however it doesn’t return anything and just modifies names directly!
Let’s look at another example of similarities and differences between functions and methods
Let’s say we want to add multiple new items to names. We could call .append once for each item, but that’s annoying…
So lists have another method called .extend that expects the input to be list
names.extend(["new name 1", "new name 2", "new name 3"])
namesWe could achieve the same functionality by writing a function instead
def extend(list_1, list_2):
return list_1 + list_2 # adding lists in Python combines them!
combined = extend(["Eshin", "Emma", "Justin", "Aaron"], ["Jacob", "Jinwoo"])
combinedNotice the similarities and differences:
- both
.extendandextendtake arguments and create a combined list - because
.extendis a method, i.e. “belongs” tonameswe only need to pass in the second list we want andnamesgets modified in-place - because
extendis a function, it doesn’t “keep track” of any lists and so we always need to pass in everything we want to combine
You can see all the various methods that an object has in Python by typing . and checking out the scrollable auto-complete or pressing cmd+. if it doesn’t show-up automatically
Try finding a method that let’s you sort the items in a list alphabetically and running it on combined
# You code hereMany of simpler variable types we met earlier also have a variety of helpful methods:
# String methods
species = "chinstrap penguin"
species.upper() # convert to uppercase
species.capitalize() # capitalize first letter
species.split() # split into words
# Number methods
score = 100
score.is_integer()3.2 Dictionaries
The other most common way to store multiple variables is to order them by a key rather than position.
In Python we can do this with a dictionary, which we can create and index using curly braces {}.
Dictionaries store their entries using using key : value pairs, so we can use a key to index a value
# Create a dictionary
character = {
"name": "Mickey",
"favorite_food": "cheese",
"age": 391,
"friends": ["Minny", "Goofy"],
}
characterNotice that each value, e.g. Mickey, cheese, etc is associated with a key, e.g. name, favorite_food
This allows to get specific values by referring to their key
character["name"]
character["favorite_food"]This makes dictionaries different from lists, because values have no concept of “position”
The code below gives us an error because the character dictionary is searching for a key named 0, which doesn’t exist!
# Try to get the "first" value of the dictionary
character[0]The only way to retrieve values from character is to use one of the valid keys
We can combine what we just learned about methods to checkout the keys and values of character
character.keys()
character.values()Dictionaries are most useful when you want to store multiple variables together and be able to easily reference them by names that you control (keys).
In other cases, lists are all you need!
3.3 Looping
Now that we have a way to store multiple variables using a list or dictionary we can use looping as a way to step through each item and perform some operation
In Python we iterate or “loop” over a variable that contains other variables using for loops Let’s see an example combining what we learned about methods to grow an empty list with lowercase versions of the items in penguins
# Create list
penguins = ["Adelie", "Gentoo", "Chinstrap"]
# Setup an empty list to store our results
penguins_lowercase = []
# Loop through it
for this_penguin in species:
# We indent code just like we do for if/elif/else
# Use the .lower method that all strings have
new_item = this_penguin.lower()
# Use the .append method to add the item to the list
penguins_lowercase.append(new_item)
# Show result
penguins_lowercaseIn the code above, Python will repeat everything indented inside the for loop, setting the value of this_penguin to the next item in the list after it’s done.
When there are no more items, the for loop ends and the code below it runs.
To loop over dictionaries we can use the .items() method which returns key, value pairs
for key, value in character.items():
print("The key is:", key)
print("The value is: ", value)Let’s take a look at character.items() more carefully to understand how we can loop over multiple things at the same time
Notice that it has a type called dict_items that looks just like a list [] where each item is ('key', 'value')
The pattern we used above: for key, value
separately sets key to the first variable in each pair, and value to the second variable
items = character.items()
itemsWhile the .items() method is specific to dictionaries we can use this same pattern for item_list1, item_list2
along with the the built-in zip() function to generally loop over multiple things at the same time
Notice how zipped looks like items from above, organized as score, age pairs
subject_scores = [76, 88, 100]
subject_ages = [25, 18, 65]
# We pass the result of zip() to the list() function so we can view it
# for doesn't need us to do this as we'll see below
zipped = list(zip(subject_scores, subject_ages))
zippedMeaning we can loop over them together:
# for can understand zip() without list()
for score, age in zip(subject_scores, subject_ages):
print("This score: ", score)
print("This age: ", age)Of course we can do whatever we’d like inside a for loop. A common one is combining it with some logic:
# Loop with conditions
weights = [3750, 3800, 3250, 4500, 3650]
# Store how many heavy penguins we have
heavy_count = 0
# Loop over weights
for weight in weights:
# Check this weight
if weight > 4000:
# Updated the value of heavy_count
heavy_count = heavy_count + 1
# strings prefixed with f allow us to put Python code inside of {} when we print them
print(f"Found {heavy_count} heavy penguins")Another one is using break and continue to _break_out of for-loop early without running through all the items or to skip processing an item and move to the next item
# Range is a function that creates a list from 0 to N
# in this case 0-9
for i in range(10):
if i == 5:
break # stop at 5
print(i)
print("---")
# list of 0-4
for i in range(5):
if i == 2:
continue # skip 2
print(i)Recap
Try using the next few cells to play the with concepts you’ve learned so far:
- creating and working with lists
- crating and working with dictionaries
- writing
forloops
# You code here# You code here# You code here4. Getting help, handling errors, and using libraries
4.1 Getting help
These easiest way to get help from write inside a notebook is to add the special ? operator to the end of any function or method
This will pull-up the “doc-string” telling you about arguments, outputs, and other information
# Look up a function
len?# Look up the append method on our names list
names.append?4.2 Understanding errors
Python tries to be pretty helpful about telling you what went wrong and where
But initially it can be challenging and intidimating to understand these messages or “stack-trace”
Let’s try to understand one we saw earlier: trying to get a value from a dictionary using a numeric position instead of a key
character[0]In the output you can see a few important pieces:
- the line number of the code the trigger the error (line 1)
- the exact type of error triggered (
KeyError) - the error message printed after the error type (
0)
Here’s a more complicated example where we make the same error, but it’s buried within more code;
def get_value(key):
return character[key]
for i in range(10):
value = get_value(i)
print(value)No we see more output, but we can still identify the same important pieces:
- line number we now have 2 locations:
- line 5 inside the
for loopbecause we callget_value(i) - line 2 inside
get_valuewhich becausecharacter[key]is using a number forkey
- line 5 inside the
- type
KeyErrorbecause0isn’t a valid key - message printed after the error type (
0)
In Python we call error outputs a “stack-trace” because Python is tracing the error through the layers of code for us!
Line 5 is where Python crashed, but because that line calls a function, Python can point to the exact problematic line (2) within the function.
There are many other types of errors you can produce and you’ll likely see many of the following:
# TypeError
# Using + with mixed types doesn't make sense!
1 + "name"# IndexError
letters = ["a", "b"]
# The 3rd value doesn't exist!
# The index/position is beyond the length of the list!
letters[2]# SyntaxError
# We're missing a ":" after names
# These basically typos that are easiest to spot because you'll see squigglies showing you
if names
print("We have names")# NameError
# We haven't created an x variable!
xIf you can’t easily figure out what the issue is from the stack-trace that Python gives you, try looking it up on google or chatgpt.
Both the internet and LLMs are very good at giving you help for common errors because so many people have encountered them before!
You can also check out this brief guide on debugging errors for additional resources
Next Steps
Congratulations! You’ve covered the absolute essentials to start your Python journey.
Here are a few additional to continue learning: