The course website contains all important information about the course:
- Syallabus
- Course Calendar
- Assignments
- Remote Learning: Panopto Videos
- Remote Learning: Zoom live Videos
- Piazza
- Gitlab (Repositories)
Course Website: http://people.cs.uchicago.edu/~lamonts/classes/2019_2020/fall/mpcs51042/index.html
In my opinion, here are the reasons to learn Python:
Easy to learn:
Python's syntax is relatively simple with very few rules and special cases. The syntax allows for rapid prototyping, which allows for you to focus on what you want to do with the code and quickly experiment and implement ideas. You typically spend less time in Pythong worrying about language intricacies.
It is an open source, general-purpose and multiparadigm programming language:
Allows for implementing applications in many different domains (e.g., data science, artificial intelligence, web development, systems level scripting, etc). With more than 20 years of development, it is available on many different platforms (Linux, Windows, MacOS, etc.) and works as intended on these platforms. It also allows programmers to develop code using a combination of different programming paradigms such as procedural, object-oriented, and functional programming.
Continues to be a popular language:
-- https://stackoverflow.blog/2017/09/06/incredible-growth-python/
-- https://stackoverflow.blog/2017/09/06/incredible-growth-python/
You want a job in Data Science and Artificial Intelligence (especially Machine learning):
Python is the preferred programming languages in many regards in the data ccience and machine learning fields. With its vast number of packages in these areas, other languages cannot match the level of sophicastion of these packages bring to the language.
Note: There are areas where using Python would probably not be appropriate. Throughout this course, I'll make sure to talk about the pitfalls of the language as well.
When learning new languages, the traditional first program is the one that prints the text "hello world" to the terminal. Here's the implemenetation in Python.
# This will print "Hello World" to the console
print("Hello World")
Notes:
print is a built-in function. It takes a value and prints it to the terminal.#. In Python, there are two ways of running code:
Using an interactive prompt via the Python interpreter (i.e., a program that executes other programs. Using the interpreter in its interactive mode, will execute the instructions (i.e., the statements in your code) one by one. You write a line of code, you hit enter, and the interpreter executes that line of code and shows the result (if any).
The interpreter is accessible via the terminal program on your machine.
The interpreter is also known as a RELP (read, eval, print, loop)
The REPL is great for learning, experimenting, and testing Python code
Hello World Demo: Create a blank file called hello.py, write print("Hello, world!") inside it, and then run::
ipython hello.py
Note: We will discuss how the interpter works in the coming weeks.
As stated before, the REPL a good tool for experimenting with code interactively. Once your code becomes complex then you will want to save it as a Python program. These programs are saved in a .py file (or a collection of .py files).
Python2 and Python3 are the two main versions of Python. However, the end of life for Python2 happend at the beginning of 2020; therefore, Python3 is the future of the language and is the one we will use.
You all have prior programming experience; therefore, I want to quickly go over the syntax for common programming constructs in Python.
We will learn about more advance features of these constructs over the next few weeks. For now, I will just show you the basic syntax for them.
A variable in Python has the following syntax:
variable_name = expression
Notes:
variable_name should be in all lowercase letters with words seperated by an underscore.CONSTANT_NAME). expression is just a combination of one or more constants, variables, operators, functions, etc. x = 1234
y = True
z = 4
var = x + z
var
Notice how I did not specify the type for the above variables. This is because Python is a dynamically-typed language: it automatically determines and keeps track of varible types without requiring a type declaration. Other languages, like Java and C/C++, are statically-typed and require the programmer to specify the type of every variable. For example, in Java we would need to declare the variable x from above as:
int x = 1234;
At any point you can determined the infered type for a variable by using the type(variable_name) function.
type(x)
type(y)
We will learn more about Python's type system next week.
As with other programming languages, Python provides built-in core types:
int, float, complexstr, list, tuple boolsetdictIn week #1, we will cover the numeric, sequence, and boolean types. Next week, we will talk about the set and mapping types.
The following diagram provides examples of numeric values in Python:
-- Learning Python 2013
Specialized numeric types (e.g., Fraction Decimal) can be imported from the standard library.
Explicit conversion of the numeric types are done using the
int(), float() and complex() functions.
x = 235
x
y = float(x) #convert the integer value for x into a float value
y
The following diagram provides examples of defining numeric expressions using the common numeric operators provided in Python:
-- Learning Python 2013
Note: the evaluation of numeric expressions are done similiarly to the order of precedence in other languages:
Order of Precedence in Python:https://docs.python.org/3/reference/expressions.html#summary
Mixed arithmetic is allowed. Python converts operands up to the type of the most complicated operand. Converting up is also done at the expression level.
34 + 3.2 # Conversion = 34.0 + 3.2 = 37.2
(3 + 5) - 4.0 # Conversion = (3.0 + 5.0) - 4.0 = 4.0
As with other languages, the values that represent booleans are defined as:
True False| Syntax | Definition |
|---|---|
x > y |
True if left operand is greater than the right |
x < y |
True if left operand is less than the right |
x == y |
True if both operands are equal |
x != y |
True if both operands are not equal |
x >= y |
True if left operand is greater than or equal to the right |
x <= y |
True if left operand is less than or equal to the right |
3 <= 12
5 == 7
| Syntax | Definition |
|---|---|
x and y |
True if both the operands are true |
x or y |
True if either of the operands is true |
not x |
True True if operand is false |
3 <= 12 and 4 >= 4
b = False
not b
As with many other languages, there is a value in Python that represents the absence of a value. In Python, this is known as the None type and has a single value as None.
We will talk more about the use cases of None as we progress in the course.
x = None
print(x)
Sequences allow you to store multiple values in an organized and effcient way. The sequence types we will examine will be str, list, and tuple.
Strings (i.e., str type in Python) are used to record both textual information and arbitrary collections of bytes.
String are immutable sequences: positionally ordered collection of other objects that cannot be modified.
String transformation operations/functions always produce new str values.
There are many different ways of defining a str in Python
# Single quote literal
# Allows you to embed double quotes (") inside the string
str1 = 'spa"m'
print(str1)
type(str1)
# Double quote literal
# Allows you to embed double quotes (') inside the string
str2 = "spa'm"
print(str2)
# Triple quote literal (multi-line strings)
# Multi-line strings include the newlines
str3 = '''Here is a list of names all on different lines
""Bob"
"Sally"
"Joe"
"Karen"'''
print(str3) # Notice the newlines are preserved
# Double quote version (multi-line strings)
# Allows for escaping single quotes ''
str3 = """Here is a list of names all on different lines
'Bob'
'Sally'
'Joe'
'Karen'"""
print(str3)
# As with other languages, you can have escape characters in strings
# \t - tab character
# \n - newline character
# Many others: https://www.w3schools.com/python/gloss_python_escape_characters.asp
str5 = "Bob\tSally\nJoe"
print(str5)
str6 = 'Bob\tSally\nJoe'
#Below is an example of "raw strings", which include escape characters
# a raw string always begins with a "r" before the string syntax
str7 = r'C:\new\test.py'
non_raw_str7 = 'C:\new\test.py'
#Notice the strings actually contain the \n and \t in the ouput versus
# the non-raw versions
print(str7)
print("----")
print(non_raw_str7)
Another way to define strings is to format them. Formattting allows you to incorporate values from other types to produce a new string.
The following are a few ways to produce a formatted string.
# By relative position '{}' using the "format" method.
# Auto-conversion for non-strings.
f_str = 'Address = {} {} {}'.format(4543, 'Elaine', 'Street')
print(f_str)
# By absolute position '{index}', where index is an integer
f_str2 = 'Hello {2}, {0} and Bye {1}'.format('Bob','Sally','Joe')
print(f_str2)
# By keyword. Specify keyword first followed by the value.
f_str3 = 'Address = {num} {name} {suffix}'.format(num = 4543, name = 'Elaine', suffix = 'Street')
print(f_str3)
# New in Python 3.6+: formatted string literals.
# IMPORTANT - Note the 'f'. This 'f' is required!
#Define a few variables
num = 4543
name = 'Elaine'
suffix = 'Street'
# Directly place your expressions inside the curly braces
f_str4 = f'Address = {num} {name} {suffix}'
print(f_str4)
The string formatting method includes additional options for formatting:
Documentation:
One of the most useful sequence types are lists (list) and they are:
A great data structure if you need to hold a collection of positionally-ordered and arbitrarily-typed values.
Mutable (i.e., they can be modified in-place)
Dynamically Resizable (i.e., they can be shorten or extended as needed)
There are many different ways to define a list in the Python. The following code sections provide a few ways.
# Empty List
lst = []
lst
# Defining a list with specified items
# Remember a list can contain values of different types.
lst = [123, 'abc', 1.23]
lst
# Nested lists (lists within lists)
lst = ['Bob', 40.0, ['dev', 'mgr']]
lst
# Empty Tuple
tup = ()
tup
# A one-item tuple (not an expression)
tup = (0,)
tup
# Asside: Why couldn't we just use say (0)?
# A four-item tuple
tup = (0, 'Ni', 1.2, 3)
# Another four-item tuple (same as prior line) without the need for the parenthesis
tup2 = 0, 'Ni', 1.2, 3
print(tup)
print(tup2)
Aside: It seems that lists and tuples are the same. They both hold a collection of values but they have slightly different syntax. When should you use one over the other? We will talk about this next week.
All sequence types (e.g., str, list, tuple) support a series of useful operations:
Length (len):
- Determines the size of a sequence value.
- The result is an integer value.
Concatenation(+):
Repetition(*):
Containment(in):
# Length
# Strings
print(len('spam'))
print('---')
# Lists
lst = ['a', 'b', 1]
print(len(lst))
print('---')
# Tuples
tup = (1,2,True,False,"Hi")
print(len(tup))
# Concatenation
# Strings
str1 = 'hello ' + 'world ' + 'helloworld'
print(str1)
print('---')
# Lists
lst = ['a']
new_lst = lst + [4, 5, 6]
print(new_lst)
print('---')
# Tuples
print((1, 2) + (3, 4))
# Repetition
# Strings
t = 'hi' * 4
print(t)
print('---')
# Lists
new_lst = lst * 2
print(new_lst)
print('---')
# Tuples
tup = (1,2)
new_tup = tup * 4
print(new_tup)
# Containment
# Strings
print('M' in 'MPCS')
print('T' in 'MPCS')
print('CS' in 'CS') # Substring containment example
print("---")
# Lists
print(4 in lst) # 4 is not in the list
print ('a' in lst) # 'a' is iniside the list
print('---')
# Tuples
tup = (1,2,True,False,"Hi")
print("Bob" in tup)
print("2" in tup)
print(1 in tup)
Sequence types provides the indexing notation (S[i]) to fetch a single item from the a sequence value.
# Sequence Indexing is zero-based (i.e., the first item in the sequence is at index 0)
# Strings
str1 = 'spam'
print(str1[0])
print(str1[2])
print('---')
# Lists
lst = ['a', 'b', 1]
print(lst[0])
print(lst[2])
# Since Lists are mutable we can use indexing to update the elements.
# Remember you cannot due the following with strings or tuples because they are immutable!
# If you try to update immutable sequences then an error will be raised.
print(lst) # Before update ['a','b', '1']
lst[0] = 3
lst[1] = 5
print(lst) # After updating [3,5,1]
print('---')
# Tuples
tup = (1,2,True,False,"Hi")
print(tup[3])
print(tup[2])
# Python allows for "backwards indexing". Placing a "-"
# before the integer index will force indexing to begin from the end
# of a sequence.
# Strings
str1 = 'spam'
print(str1[-1]) # 'm' is the last character (-1)
print(str1[-2]) # 'a' is the second to the last character (-2)
print('---')
# Lists
lst = ['Bob', ['a','b','c'], 5, 6]
print(lst[-1]) # 6 is the last element in the list (-1)
print(lst[-3]) # ['a','b','c'] is the third to the last element (-3)
# Since Lists are mutable we can use indexing to update the elements.
# Remember you cannot due the following with strings or tuples because they are immutable!
# If you try to update immutable sequences then an error will be raised.
lst[-1] = "Hello"
lst[-3] = "World"
print(lst)
print('---')
# Tuples
tup = (1,2,True,False,"Hi")
print(tup[-1]) # "Hi" is the last element in the list (-1)
print(tup[-2]) # False is the second to the last element (-2)
Slicing is a from of sequence indexing, where it returns an entire section (i.e. a copy) of a sequence.
S[i:j]): S is a sequence value i) is inclusive (defaults to zero if not specified)j) is nonclusive (defaults to len(S) if not specified)Note: There is an extended version of sequence slicing. We will talk about that next week.
S = 'MPCS51042'
lst = ['Bob', 'Sally', 'Tina']
tup = (1,2,3,4,5)
# Fetches items at offsets 1 up to but not including 3
print(S[1:3])
print('---')
print(lst[1:3])
print('---')
print(tup[1:3])
# Fetches items at offset 1 through the end (the sequence length)
print(S[1:])
print('---')
print(lst[1:])
print('---')
print(tup[1:3])
# Fetches items at offset 0 up to but not including 3
print(S[:3])
print('---')
print(lst[:3])
print('---')
print(tup[:3])
# Fetches items at offset 0 up to but not including the last item
print(S[:-1])
print('---')
print(lst[:-1])
print('---')
print(tup[:-1])
# Creates a copy of S (i.e, [0, len(S)])
print(S[:])
print('---')
print(lst[:])
print('---')
print(tup[:])
Python provides collection of methods (i.e., functions that act on values) that create new strings based on the behavior of the method.
Syntax: str_value.method_name(arg1,arg2,...)
str_value - a string value or variable .method_name - the notation to call a specific string method_name. For example,
upper : capitalizes all the letters of the string. lower : lower cases all the letters of the string. split : returns a list of words in a string where default separator is any whitespace. find : returns the lowest index of a particular substring in a string. If the substring is not found, -1 is returned.replace : Replace all occurences of a substring with a new substring.strip : returns a copy of the string with the leading and trailing characters removed. Default character to be removed is whitespace.
More string methods : https://docs.python.org/3/library/stdtypes.html#string-methods
(arg1,arg2,...) - the arguments for that method. spam = "spammy"
# Replace all mm with xx in S
new_spam = spam.replace('mm', 'xx')
print(new_spam)
print(spam) # Remember old string is not modified. Strings are immutable
spam = '----SPAM----SPAM----'
#Search for the index of the first occurence of "SPAM"
where = spam.find('SPAM')
print(where)
words = 'abc edf g'
# Creates a list of strings. The words are deliminted by default via a whitespace
lst = words.split()
print(lst)
print('---')
words = "abc,efg,g"
lst = words.split(",") # Can specify the deliminter
print(lst)
# If possible, you can convert any value into a str value by using the "str()" conversion function.
str1 = str(1.234)
str2 = str("True")
str3 = str([1,2,3])
print(str1)
print(str2)
print(str3)
print(type(str1))
print(type(str2))
print(type(str3))
Below are some common methods associated with mutable sequences. All of these methods change the sequence in-place.
-- https://docs.python.org/3/library/stdtypes. html#mutable-sequence-types
lst = [1,2,3,4]
lst.append("hi")
lst.append(True)
print(lst)
lst.pop() # Removes last element
print(lst)
There is one more important immutable sequence type to discuss range. The range type is an immutable sequence of numbers and is commonly used for looping a specific number of times in for loops.
Syntax: range(stop) or range(start,stop) or range(start,stop,step):
start: The value of the start parameter (or 0 if the parameter was not supplied) inclusive stop : The value of the stop parameter exclusivestep : The value of the step parameter (or 1 if the parameter was not supplied)range type implementes all common sequence operatrions except for concatenation and repetition range type is that it always take the same (small) amount of memory, no matter the size of the range it represents. r = range(6) # Contains an ordered collection of the values: 0,1,2,3,4,5
print(r[0])
print(r[len(r) - 1]) # Last value in the range
print('----')
r = range(1,4) # Contains an ordered collection of the values: 1,2,3
print(r[0])
print(r[-1])
print('----')
# Printing the range value does not show you the contents because the values are generated when needed
print(r)
# You can convert it into a list to see the values
print(list(r))
Python uses the usual flow control statements known from other languages with some caveats
Unlike many other languages (C,C++,Java), Python does not use braces {} to designate code blocks.
Python uses indentation to signify code block boundaries.
Statements indented to the same distance belong to the same block of code.
Blocks end:
Examples of indented statements: for, while, if
Deeply nested blocks are further indented to the right
The if statement allows for selecting from alternative actions based on conditional expression results.
Syntax:
if condition:
statement1
statement2
elif condition2: #Else if clause, if the first condition fails then we try the next else if clause
statement3
else:
statement4
Notes:
:) after the end of the condition (or the else keyword) to signal the beginning of the clause block elif and else statements are optional.if, elif, or else must be indented over 4 spaces.bmi = 31
#Determine the BMI category
if bmi < 18.5:
print("Underweight")
elif bmi >= 18.5 and bmi < 24.9:
print("Normal weight")
elif bmi >= 24.9 and bmi < 29.9:
print("Overweight")
else:
print("Obese")
The for statement is a looping statement that steps through items in any sequence or iterable object (more on this later...)
Syntax:
For every iteration of the below for loop, it assigns the items in the sequence (or iterable object) to item (one by one) and then executes the loop body.
for item in sequence:
statement1 # Statement1 is executed every iteration
if cond_exp:
break # Immediately goes to statement4
if cond_exp:
continue # Immediately goes to the next iteration
statements2 # Statement2 is executed if the continue or break was not encountered.
else:
statement3 # Statement3 is executed if loop exits without a break
statements4 # Statement4 is executed no matter how the loop exts
bmis = [12,23.45,17.56,32.34,5.6]
for bmi in bmis:
category = ''
#Determine the BMI category
if bmi < 18.5:
category = "Underweight"
elif bmi >= 18.5 and bmi < 24.9:
category = "Normal weight"
elif bmi >= 24.9 and bmi < 29.9:
category = "Overweight"
else:
category = "Obese"
output = f'BMI = {bmi}, Category = {category}'
print(output)
else:
print("Processed all bmis successfully")
# Useful For loop Idioms
# Iterating over a range of numbers using range
for num in range(10):
print(num)
# Iterating over first 20 even numbers
for even_num in range(2,21,2): # 21 because the stop is exclusive
print(even_num)
# Iterating over a collection of items and their indicies
# Call the "enumerate" function on the sequence
bmis = [12,23.45,17.56,32.34,5.6]
for index,element in enumerate(bmis):
print(f'Index = {index}, BMI = {element}')
A while loop repeatedly executes a block of statements as long as its condition expression evaluates to a True value.
while condition:
statement1 # Statement1 is executed every iteration
if cond_exp:
break # Immediately goes to statement4
if cond_exp:
continue # Immediately goes to the next iteration
statements2 # Statement2 is executed if the continue or break was not encountered.
else:
statement3 # Statement3 is executed if loop exits without a break
statements4 # Statement4 is executed no matter how the loop exts
bmis = [23.45,18.67,32.34,5.6]
index = 0
# Keep looping while all values are not underweight
while True:
bmi = bmis[index]
index = index + 1
if bmi < 18.5:
break
elif bmi >= 24.9:
continue
#Only printing normal weight bmis
print(f'BMI = {bmi}, Category = Normal Weight')
A function is a set of statements that can be called more than once.
Benefits of functions:
def name(arg1, arg2, ..., argN):
'''
Description of function task
Inputs:
arg1(type): description of arg1
arg2(type): description of arg2
...
argN(type): description of argN
Outputs:
Description of what this function returns
'''
statement1
statement2
...
return value
The following are the steps to define a function:
def defines a new function called name.return can appear anywhere in body of functionNone by default. def bmi_category(bmi):
'''
Calculates the bmi category given a bmi value
Inputs:
bmi(float): the bmi value to use
Output:
Returns the bmi category for the bmi value.
'''
category = ""
if bmi < 18.5:
category = "Underweight"
elif bmi >= 18.5 and bmi < 24.9:
category = "Normal weight"
elif bmi >= 24.9 and bmi < 29.9:
category = "Overweight"
else:
category = "Obese"
return category
# Call the function by specifying its name followed by any necessary arguments to the function
print(bmi_category(17.34))
print(bmi_category(24.53))
The pass statement useful when you are not ready to write the implementation for a code block. The statement allows the interpreter to perform no operation and to continue processing the next statement.
def bmi_category(bmi):
'''
Calculates the bmi category given a bmi value
Inputs:
bmi(float): the bmi value to use
Output:
Returns the bmi category for the bmi value.
'''
pass # IMPLEMENT ME
print("----begin processing bmi values---")
print(bmi_category(17.34)) # No error, prints None because we do not have a return statement so None is returned
print(bmi_category(24.53)) # No error, prints None because we do not have a return statement so None is returned
print("Done.")
# Pass can be used in any code block statements: loops, classes (later), functions, etc.
print("Start")
for r in range(10):
pass
print("Done")
As stated earlier, Python programs can be defined in .py files. These files are also known as modules. A module is a file containing Python definitions and statements. The file name is the module name minus the .py suffix.
What if you want these files to interact with each other (i.e., call functions, use variables from different modules)?
import statement.
- ``import module_name``
- Import statements should be placed at the beginning of a python file.
- default way to access variabls, and statements is to use dot notation, where you specify the name of the module followed by a period, and then the code you want to access.
- ``module_name.variable`` or ``module_name.func(arg1,arg2,...)``
Python provides an extensive amount of predefined modules as part of its standard library. We will look at the math module and see how we can import and use the code within that module.
# 1st import the math module
import math
print(math.pi) # Print the constant PI from the math module.
print(math.fabs(-232)) # Call the floating point abs function from the math module
In the following weeks, we will learn more about modules and ways to use them efficiently.