![]() | Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved MIT license. (c) Kyle T. Mandli |
from __future__ import division, print_functionNote to lecturers: This notebook is designed to work best as a classic Jupyter Notebook with nbextensions
hide_input: to hide selected python cells particularly for just plotting
RISE: Interactive js slide presentations
Discussion 1: Introduction to Python¶
So you want to code in Python? We will do some basic manipulations and demonstrate some of the basics of the notebook interface that we will be using extensively throughout the course.
Objectives:¶
Provide overview of simplest data types and flow control available in Python 3
Provide a few practice problems
set up for Homework 0 to debug the homework submission system and introduce working with Jupyter notebooks
Topics:¶
Math
Variables
Lists
Control flow
Coding style
Other data structures
Jupyter notebooks/Jupyter Lab
There is considerable online documentation and tutorials for python.
Other intros:¶
Python Math¶
Lets start with some basic operations:
2 + 232 - (4 + 2) ** 21 / 24.0 + 4.0 ** (3.0 / 2.0)Note: See full list of operators supported in python here
Good practice to just add a decimal after any number you really want to treat as a float.
Additional types of numbers include complex, Decimal and Fraction.
3 + 5jNote that to use “named” functions such as sqrt or sin we need to import a module so that we have access to those functions. When you import a module (or package) in Python we are asking Python to go look for the code that is named and make them active in our workspace (also called a namespace in more general parlance). Here is an example where we use Python’s builtin math module:
import math
math.sqrt(4)math.sin(math.pi / 2.0)math.exp(-math.pi / 4.0)Note that in order to access these functions we need to prepend the math. to the functions and the constant . We can forgo this and import all of what math holds if we do the following:
from math import *
sin(pi / 2.0)Notes:
import *is discouraged, particularly if you only need a few functions or you will be mixing with other modules that definesin, for examplenumpy.sin()andmath.sin()have somewhat different functionality.if you only want a few functions from math use
from math import sin, cosmany of these functions always return a
floatnumber regardless of their input.
Variables¶
Assign variables like you would in any other language:
num_students = 126
room_capacity = 120
(room_capacity - num_students) / room_capacity * 100.0As indicated in the previous section, there are many different data types. For example, a variable could be an integer, a floating point number, a string, or numerous other basic types. Python will determine the data type based on how you enter it.
In the following example, three different variables are defined, and the type associated with each variable is printed.
number = 5
ratio = 0.15
description = "The ratio is"
doit = False
print(number, type(number))
print(ratio, type(ratio))
print(description, type(description))
print(doit, type(doit))The data type can be explicitly defined using the float() and int() commands.
number = int(5.0)
ratio = float(0.15)
print(number, type(number))
print(ratio, type(ratio))Note: if you are testing for the type of a python object you should use isinstance
x = True
if isinstance(x, str):
print("{} is a string".format(x))
else:
print("{} is not a string, it is type: {}".format(x, type(x)))One thing to be careful about is that Python is case sensitive.
N = 20
n = 10
print(N, n)Lists¶
One of the most useful data structures in Python is the list.
grades = [90.0, 67.0, 85.0, 76.0, 98.0, 70.0]Lists are defined with square brackets and delineated by commas. Note that there is another data type called sequences denoted by ( ) which are immutable (cannot be changed) once created. Lets try to do some list manipulations with our list of grades above.
Access a single value in a list
print(grades)grades[1]Note that Python is 0 indexed, i.e. the first value in the list is accessed by 0. Reverse indexing is done using negative value starting from -1 which corresponds to the last element
grades[-2]Find the length of a list
len(grades)There are multiple ways to append values into a list.
print(grades)grades = grades + [62.0, 82.0, 59.0]
print(grades)grades.append(88.0)
print(grades)You can use the standard indexing method shown above to change a value within the array.
grades[1] = 68.0
print(grades)Slicing is another important operation
print(grades)grades[2:5]grades[0:4]grades[:4]grades[4:]Note that the range of values does not include the last indexed! This is important to remember for more than lists but we will get to that later.
grades[4:11]Another property of lists is that you can put different types in them at the same time. This can be important to remember if you may have both int and float types.
remember = [int("2"), 2, 2.0, "2.0"]
print(remember)A list can also hold any data type or data structure inside it, for example a list inside a list (referred to as nested lists) is helpful in defining matrices (although we will find a better way to do this later).
matrix_a = [[1], [2], [3]]Finally, one of the more useful list creation functions is range which creates a list with the bounds requested. This creates a special type within Python, but it acts like an array.
values = range(3, 7)
print(values, type(values))print(values[0], values[1], values[2], values[-1])for i in range(3, 7):
print(i)Comments¶
Comments can be added to code using the # character. Anything after # on a line is ignored.
# Set up the parameters associated with the partition
N = 10 # Number of partitions to use
b = 1.0 # The right endpoint of the interval
a = 0.0 # The left endpoint of the interval
delta_x = (b - a) / float(N) # The width of each interval
print("The interval width is {0}".format(delta_x))The python style guide PEP 8 however discourages in-line comments
Control Flow¶
In this section a number of different ways to control and define which commands are executed are given. The commands include conditional expressions like ‘if’ blocks that decide individual sets of commands to execute. It also includes ‘for’ loops which define a sequence of commands to execute in order. Finally, the ‘while’ loop is given which will loop through a set of commands until some condition is met.
if¶
This is the basic logical control. A set of instructions is executed if a given condition is met. Note that Python decides what set of commands to execute based on how the code is indented. The ‘{’ and ‘}’ characters have a very different meaning in Python than in C, C++, or Java.
Note: See full list of control flow supported in python here
x = 5
if x > 5:
itsBig = True
print("x is greater than 5")
elif x < 5:
itsBig = False
print("x is less than 5")
else:
itsBig = not True
print("x is equal to 5")
print("The value of itsBig is {0}".format(itsBig))for loops¶
The for statements provide the most common type of loops in Python. The idea is that a set of commands will be repeated for a fixed number of times. The command requires a variable that can be iterated over, and each time the loop repeats a new value from the variable is used. For example, if an array is given the for loop will iterate over each value within the array. (there is also a while construct).
accumulator = 0
for i in range(-10, 5, 3):
accumulator += 1
print(i)
print("The number of times the loop repeated is {0}".format(accumulator))iterating over lists¶
It is often useful to iterate over members of lists directly
for animal in ["cat", "dog", "chinchilla"]:
print(animal)enumerate is also a very useful builtin when you need both an index and a list member
for i, animal in enumerate(["cat", "dog", "chinchilla"]):
if i % 2 == 0:
print(i, animal)The above can be written in a single line and also save the outputs to a new list, by using list comprehension
animal_new = [animal.capitalize() for animal in ["cat", "dog", "chinchilla"]]
print(animal_new)Careful about assignment of objects¶
Here we’ll creat a list from a range using list comprehension
range_list = [i for i in range(3, 7)]
print(range_list, type(range_list))create a “new” list by assignment
x = range_list
print(x, type(x))change an element in x
x[2] = 12
print(x)print(range_list)A quick exercise:¶
do you remember the list remember (which is a list of different types)? write a one line list comprehension to return a list of types in remember
print(remember)Related to the for statement are the control statements break and continue. Ideally we can create a loop with logic that can avoid these but sometimes code can be more readable with judicious use of these statements. This is especially true for while loops and separate checks have to be made for iteration counts.
# Naive prime number check
for n in range(2, 10):
is_prime = True
for k in range(2, n):
if n % k == 0:
print(n, "equals", k, "*", n / k)
is_prime = False
break
if is_prime:
print("%s is a prime number" % (n))The while Loop¶
The set of commands in a while loop are executed while a given condition is True.
top = 10
bottom = 5
while top > bottom:
print("top: {0}, bottom: {1}".format(top, bottom))
top -= 1
bottom += 1The pass statement might appear fairly useless as it simply does nothing but can provide a stub to remember to come back and implement something.
def my_func(x):
# Remember to implement this later!
passDefining Functions¶
The last statement above defines a function in Python with an argument called x. Functions can be defined and do lots of different things, here are a few examples.
def my_print_function(x):
print(x)
my_print_function(3)def my_add_function(a, b):
return a + b
my_add_function(3.0, 5.0)A variable can be given a default value while defining the function, this value remains unchanged unless the user specifies a different value
def my_crazy_function(a, b, c=1.0):
d = a + b**c
return d
my_crazy_function(2.0, 3.0)my_crazy_function(2.0, 3.0, 2.0)my_crazy_function(2.0, 3.0, c=2.0)def my_other_function(a, b, c=1.0):
return a + b, a + b**c, a + b ** (3.0 / 7.0)
x, y, z = my_other_function(2.0, 3.0, c=2.0)
print(x)Let’s try writing a bit more of a complex (and useful) function. The Fibonacci sequence is formed by adding the previous two numbers of the sequence to get the next value (starting with [0, 1]).
def fibonacci(n):
"""Return a list of the Fibonacci sequence up to n"""
values = [0, 1]
while (next_val := values[-1] + values[-2]) < n:
values.append(next_val)
print(values)
fibonacci(100)There are several other important data structures that are useful in python including
tuples/sequences
sets
dictionaries
you can read more about them here
Exception Handling¶
Python has a very rich syntax for handling errors and exceptions which, if used sparingly can be useful when you want to fail gracefully or give the user more information about where and how a function fails.
def isstring(x):
"""function to check if x is a string
Parameters
----------
x : any python object
Returns
-------
bool
True if x is a string, False otherwise.
Raises
------
TypeError
if x is not a string
"""
if isinstance(x, str):
print("{} is a string".format(x))
return True
else:
raise TypeError("{} is not a string".format(x))
return Falsex = "Howdy"
isstring(x)If you want to catch an exception and continue execution you can use the try-except syntax
print(remember)for r in remember:
try:
isstring(r)
except TypeError as err:
print(err)help(), ? and tab are your friends¶
Use the help() function or a ? at the end of a function to see the respective function’s documentation. One could also use tab key to autocomplete and view the list of available functions.
help(isstring)A numpy example¶
the numpy (numerical python) module is a workhorse for numerical methods and scientific computation and provides a wealth of functions and objects
import numpy
x = numpy.array(range(3))
xx.mean()Coding Style¶
It is very important to write readable and understandable code.
This is a practical matter.
There are times when you have to go back and make changes to code you have not used in a long time. More importantly, coding is a shared activity, and if your code is not readable then it is not of any use.
Here are a few things to keep in mind while programming in and out of this class, we will work on this actively as the semester progresses as well. The standard for which Python program are written to is called PEP 8 and contains the following basic guidelines:
Use 4-space indentation, no tabs
Wrap lines that exceed 80 characters
Use judicious use of blank lines to separate out functions, classes, and larger blocks of contained code
Comment! Also, put comments on their own line when possible
Use
docstrings(function descriptions)Use spaces around operators and after commas,
a = f(1, 2) + g(3, 4)Name your classes and functions consistently.
Use
CamelCasefor classesUse
lower_case_with_underscoresfor functions and variables
When in doubt be verbose with your comments and names of variables, functions, and classes
Peer Review¶
To help all of us learn from each other what coding styles are easier to read we should be doing peer-reviews of the coding portions of the assignments. After the first assignment is turned in we will review a general template for code review. Please be as thorough and helpful as you can!
Example: why is this actually a poor piece of code?¶
def isstring(x):
"""function to check if x is a string
Parameters
----------
x : any python object
Returns
-------
bool
True if x is a string, False otherwise.
Raises
------
TypeError
if x is not a string
"""
if isinstance(x, str):
print("{} is a string".format(x))
return True
else:
raise TypeError("{} is not a string".format(x))
return FalseJupyter Notebooks¶
We will use a lot of Jupyter notebooks in this class for both class notes (what you are looking at now) and for turning in homework. The Jupyter notebook allows for the inline inclusion of a number of different types of input, the most critical will be
Code (python or otherwise) and
Markdown which includes
,
HTML
Jupyter notebooks allow us to organize and comment on our efforts together along with writing active documents that can be modified in-situ to our work. This can lead to better practice of important ideas such as reproducibility in our work.
Debugging¶
Debugging is one of the most critical tools we have at our disposal. Apart from standard inspection approaches (print statements) the Jupyter ecosystem has a number of ways to debug as well.
More modern versions of Jupyterlab have a built in debugger (see documentation)
For old-school Jupyter notebooks (like this one) one can access the python debugger pdb using the “magic” %pdb at the top of your notebook which will allow cause the notebook to jump into the python debugger anytime an exception is reached. This will allow you to step through your code and figure out what is wrong. If you want to step through code or just activate the trace back for the current cell use the %debug magic.
# for inline plotting in the notebook
%matplotlib inline
# debugger
%pdb
import matplotlib.pyplot as plt
import numpy
def plot_log():
figure, axis = plt.subplots(2, 1)
x = numpy.linspace(1, 2, 10)
axis.plot(x, log(x)) # <-- this line is wrong in multiple ways
plt.show()plot_log() # Call the function, generate plotPaths to debugging
Check the traceback
printstatementsUse the debugger in Jupyter Lab (>2?) or ‘debug magic %pdb’ in older versions
Try a more informative IDE like PyCharm or Visual Studio Code (aka vscode)
and if all else fails
Copy and paste your error message into Google to see if anyone else has experienced similar problems. You’d be surprised how often this works!
Search StackOverflow
Ask your favorite AI (with care)
Consult fellow classmates
Consult the TA’s and Professor (absolute last resort ;^)
Don’t forget to have fun...¶
Debugging is puzzle solving...the better you get at it, the better you can manage the frustration of numerical methods
