Python Programming*

*inspired by David Beazley's Practical Python Programming and Allen B. Downey's Think Python 2

What is Python?

OIM3600

Where is Python?

C:\Users\zli>where python
C:\Users\zli\AppData\Local\Programs\Python\Python312\python.exe
C:\Users\zli\miniconda3\python.exe

C:\Users\zli>python
Python 3.12.6 (tags/v3.12.6:a4a2d2b, Sep  6 2024, 20:11:23) [MSC v.1940 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print('hello world')
hello world
>>> 

OIM3600

Exercise: Using Python as a Calculator

Start Python and use it as a calculator to solve the following problem:

John bought 75 shares of Nvdia stock at $133.82 per share last year. Today, he sold all of his shares at $437.37. How much profit would he make?

>>> (437.37 - 133.82) * 75
22766.25
>>>

How much profit does he make after his evil broker takes their 20% cut?

>>> _ * (1 - 0.20)  # _ holds the last result.
18213.0
>>>
OIM3600

Exercise: Getting help

  • Use the help(abs) command to get help on the abs() function.
  • Use help(round) to get help on the round() function.
  • Try:
    • use help()
    • use help("for")
  • Further Resources - Official Python documentation at docs.python.org
    • You can find documentation for various Python functions and modules, including the abs() function.
OIM3600

Exercise: Tell a Joke

import json
import urllib.request

url = "https://v2.jokeapi.dev/joke/Any?safe-mode"

try:
    with urllib.request.urlopen(url) as response:
        data = response.read().decode('utf-8')
        json_data = json.loads(data)
        if json_data["type"] == "single":
            print(json_data["joke"])
        elif json_data["type"] == "twopart":
            print(json_data["setup"])
            print(json_data["delivery"])
except urllib.error.URLError as e:
    print(f"Error fetching data from URL: {e}")
except json.JSONDecodeError as e:
    print(f"Error parsing JSON data: {e}")
OIM3600

Running Python

  • Python programs always run inside an interpreter.
    • a “console-based” application that normally runs from a command shell.
      Python 3.12.6 (tags/v3.12.6:a4a2d2b, Sep  6 2024, 20:11:23) [MSC v.1940 64 bit (AMD64)] on win32
      Type "help", "copyright", "credits" or "license" for more information.
      >>>
      
    • not so user-friendly for beginners.
  • If you start typing code and press enter, they will run immediately.
    • so-called read-eval-print-loop (or REPL)
    • There is no edit/compile/run/debug cycle.
  • We will create programs in VS Code.
    • Programs are saved as .py files.
OIM3600

A Sample Program

Let's solve the following problem:

One morning, you go out and place a dollar bill (0.11 mm thick) on the sidewalk by the Prudential Tower in Boston (228 meters). Each day thereafter, you go out double the number of bills. How long does it take for the stack of bills to exceed the height of the building?

  • Solution code and output in next slides.
  • We will use this program as a guide to learn a couple of important programming concepts.
OIM3600

Solution Code

# bills.py

bill_thickness = 0.11 * 0.001  # Meters (0.11 mm)
prudential_height = 228  # Height (meters)
num_bills = 1
day = 1

while num_bills * bill_thickness < prudential_height:
    print(day, num_bills, num_bills * bill_thickness)
    day = day + 1
    num_bills = num_bills * 2

print('Number of days:', day)
print('Number of bills:', num_bills)
print('Final height:', num_bills * bill_thickness)
OIM3600

Output

>python bills.py
1 1 0.00011
2 2 0.00022
3 4 0.00044
4 8 0.00088
5 16 0.00176
6 32 0.00352
...
Number of days: 22
Number of bills: 2097152
Final height: 230.68672
OIM3600

Variables

  • A variable is a name for a value.

  • Letters (lowercase and uppercase) and underscore _ can be used.

  • Numbers can be part of the variable name, except as the first character.

  • It's essential to use a name that reflects the purpose of the variable.

  • Variable names are conventionally written in lowercase, except for CONSTANT_VARIABLEs.

    height = 228 # valid
    _height = 228 # valid
    height2 = 228 # valid
    2height = 228 # invalid
    
OIM3600

Expressions

  • An expression is a combination of values, variables, and operators.
    • A value or variable alone is considered an expression.
  • When you type an expression at the prompt, the interpreter evaluates it, which means that it finds the value of the expression.
  • Expressions are typically not written as standalone lines in a program.
    • In programs, we usually write statements instead of expressions.
OIM3600

Statements

  • A python program consists of sequence of statements:
    a = 3 + 4
    b = a * 2
    print(b)
    
  • Each statement is terminated by a newline.
  • Statements are executed sequentially from top to bottom.
  • A statement is a unit of code that has an effect, such as creating a variable or displaying a value.
    • Expressions, on the other hand, don't have any effect by themselves.
OIM3600

Comments

  • Comments are text that will not be executed.
    a = 3 + 4
    # This is a comment
    b = a * 2
    print(b)
    
  • Comments are denoted by # and extend to the end of the line.
OIM3600

Types

  • Variables in Python don't require a declared type.
  • Type is associated with the right-hand value, not the variable name.
    height = 228           # Integer
    height = 228.0         # Float
    height = 'Really tall' # String
    
  • Python is dynamically typed.
    • The perceived "type" of a variable can change during program execution.
OIM3600

Printing

  • print() always puts a newline at the end.
    print('Hello')
    print('How are you?')
    
    This prints:
    Hello
    How are you?
    
  • The extra newline can be suppressed:
    print('Hello', end=' ')
    print('How are you?')
    
    This prints:
    Hello How are you?
    
OIM3600

User input

  • To read a line of user input, use the input() function:
    name = input('Enter your name:')
    print('Hello', name)
    
    • Useful for small programs, learning exercises, or simple debugging.
    • Not commonly used in more complex or production-level programs.
  • Remember that input() always returns a string, so you may need to convert it to other types if needed.
OIM3600

Conditionals

if x < y:
    print("x is less than y")
elif x > y:
    print("x is greater than y")
else:
    print("x is equal to y")
OIM3600

Loops (using while)

  • The while statement executes a loop.
    while num_bills * bill_thickness < prudential_height:
        print(day, num_bills, num_bills * bill_thickness)
        day = day + 1
        num_bills = num_bills * 2
    
    print('Number of days', day)
    
  • The statements indented below the while will execute as long as the expression after the while is True.
  • Indentation is used to denote groups of statements that go together.
OIM3600

Exercise: The Bouncing Ball

A rubber ball is dropped from a height of 100 meters and each time it hits the ground, it bounces back up to 3/5 the height it fell.

  • Write bounce.py that prints a table showing the height of the first 10 bounces.
    1 60.0
    2 36.0
    3 21.6
    4 12.96
    5 7.776
    6 4.6656
    7 2.7994
    8 1.6796
    9 1.0078
    10 0.6047
    
OIM3600

Loops (using for)

  • Use for loop when you know how many times to repeat.
    for i in range(4):
        print('Hi')
    
    # Equivalent to this, although not exactly the same
    for i in [0, 1, 2, 3]:
        print('Hi')
    
  • Use for loop to iterate over a sequence (collections of items, such as strings, lists, tuples)
    numbers = [2, 3, 5, 7]       # numbers is a list
    for num in number:
        print(num ** 2)
    
OIM3600

Data Types

OIM3600
OIM3600

Numbers

  • Integers (int)
    a = 42
    b = -299392993727716627377128481812241231
    c = 0x7fa8      # Hexadecimal
    d = 0o253       # Octal
    e = 0b10001111  # Binary
    
  • Floating point (float)
    a = 37.45
    b = 4e5 # 4 x 10**5 or 400,000
    c = -1.345e-10
    
OIM3600

Common Arithmatic Operations

  • Common arithmatic operators:
    x + y      # Add
    x - y      # Subtract
    x * y      # Multiply
    x / y      # Divide (produces a float)
    x // y     # Floor Divide (produces an integer)
    x % y      # Modulo (remainder)
    x ** y     # Power
    abs(x)     # Absolute Value
    
  • math module
    import math
    
    a = math.sqrt(x)
    b = math.sin(x)
    c = math.log(x)
    
OIM3600

Converting Numbers

  • The type name can be used to convert values.
    a = int(x)    # Convert x to integer
    b = float(x)  # Convert x to float
    
  • Examples
    >>> a = 3.14159
    >>> int(a)
    3
    >>> b = '3.14159'
    >>> float(b)
    3.14159
    >>>
    
OIM3600

Booleans (bool)

  • Booleans have two values: True, False.
  • Commonly used in conditions (if, elif, while).
  • They can be combined with logical operators (and, or, not)
OIM3600

Comparison Operators

  • The following comparison operators work with numbers:
    x < y      # Less than
    x <= y     # Less than or equal
    x > y      # Greater than
    x >= y     # Greater than or equal
    x == y     # Equal to
    x != y     # Not equal to
    
  • These operators compare values and generate Boolean values.
OIM3600

Logical Operators

  • and: Returns True if both conditions are True.
    x = True
    y = False
    result = x and y  # result is False
    
  • or: Returns True if at least one condition is True.
    x = True
    y = False
    result = x or y  # result is True
    
  • not: Negates the value of a Boolean.
    is_sunny = True
    is_rainy = not is_sunny  # is_rainy is False
    
OIM3600

Custom Functions

  • Syntax
    def function_name(parameters):
        """docstring"""
        # function body
    
  • Example:
    def is_prime(num):
        """Check if a number is prime."""
        if num <= 1:
            return False
        for i in range(2, int(num**0.5) + 1):
            if num % i == 0:
                return False
        return True
    
OIM3600

Library Functions

  • Python comes with a large standard library.
  • Library modules are accessed using import.
  • Example:
    import math
    x = math.sqrt(10)
    
    import urllib.request
    u = urllib.request.urlopen('http://www.python.org/')
    data = u.read()
    
OIM3600

Strings

# Single quote
course = 'OIM3600'

# Double quote
title = "Computer Science"

# Triple quotes
hey_jude = """
Hey Jude, don't make it bad.
Take a sad song and make it better.
Remember to let her into your heart,
Then you can start to make it better.
"""
OIM3600

F-Strings

  • A powerful way to format strings using f-strings (formatted string literals).
  • It allows you to embed expressions inside string literals for dynamic formatting.
  • Syntax: f'{expression:format_specifier}'
  • Example:
    >>> name = 'MSFT'
    >>> shares = 100
    >>> price = 317.92
    >>> print(f'{name:>10s} {shares:10d} {price:10.2f}')
          MSFT        100     317.92
    >>> print(f'Cost = ${shares*price:0.2f}')
    Cost = $31792.00
    >>>
    
OIM3600

String Indexing

  • You use an integer index for accessing individual characters, starting at 0.
    >>> a = 'Hello world'
    >>> a[0]                # 'H'
    >>> a[4]                # 'o'
    >>> a[-1]               # 'd' (end of string)
    
  • You can also slice or select substrings specifying a range of indices with :
    >>> a[:5]               # 'Hello'
    >>> a[6:]               # 'world'
    >>> a[3:8]              # 'lo wo'
    >>> a[-5:]              # 'world'
    
  • Strings are immutable (read-only).
    a[0] = 'h'              # TypeError
    
OIM3600

String Common Operations

# Concatenation (+)
a = 'Hello' + 'World'   # 'HelloWorld'
b = 'Say ' + a          # 'Say HelloWorld'

# Length (len)
s = 'Hello'
len(s)                  # 5

# Membership test (`in`, `not in`)
t = 'e' in s            # True
f = 'x' in s            # False
g = 'hi' not in s       # True

# Replication (s * n)
rep = s * 5             # 'HelloHelloHelloHelloHello'
OIM3600

String Methods

  • Example: stripping any leading / trailing white space

    s = '  Hello '
    t = s.strip()     # 'Hello'
    
  • Example: Case conversion

    s = 'Hello'
    l = s.lower()     # 'hello'
    u = s.upper()     # 'HELLO'
    
  • Example: Replacing text

    s = 'Hello world'
    t = s.replace('Hello' , 'Hallo')   # 'Hallo world'
    
OIM3600

More String Methods

s.endswith(suffix)     # Check if string ends with suffix
s.find(t)              # First occurrence of t in s
s.index(t)             # First occurrence of t in s
s.isalpha()            # Check if characters are alphabetic
s.isdigit()            # Check if characters are numeric
s.islower()            # Check if characters are lower-case
s.isupper()            # Check if characters are upper-case
s.join(slist)          # Join a list of strings using s as delimiter
s.lower()              # Convert to lower case
s.replace(old,new)     # Replace text
s.rfind(t)             # Search for t from end of string
s.rindex(t)            # Search for t from end of string
s.split([delim])       # Split string into list of substrings
s.startswith(prefix)   # Check if string starts with prefix
s.strip()              # Strip leading/trailing space
s.upper()              # Convert to upper case
OIM3600

Exercise: Strings

symbols = 'AAPL,MSFT,GOOG,AMZN'

  1. symbols[0], symbols[-1], ...
  2. Modify symbols to make it become 'AAPL,MSFT,GOOG,AMZN,TSLA'?
  3. 'GOOG' in symbols? 'AA' in symbols?
  4. symbols.lower()?
  5. symbols.find('MSFT')?
  6. symbols.strip('A')?
OIM3600

Lists

  • Python's primary type for holding an ordered collection of values.
  • Creating a list:
    nums = [2, 3, 5, 7, 11, 13, 17, 19]
    
  • Lists can also be created via other methods.
    • e.g., list(range(1, 6))
    • A string can be split into a list using the split() method:
      >>> symbols = 'AAPL,MSFT,GOOG,AMZN'
      >>> stocks = symbols.split(',')
      >>> stocks
      ['AAPL', 'MSFT', 'GOOG', 'AMZN']
      
OIM3600

List Common Operations

  • Add a new item using append()
    stocks.append('TSLA')           # Adds at end
    stocks.insert(2, 'NVDA')        # Inserts at given position
    
  • Use + or extend() to concatenate lists
    s = [2, 3, 5, 7]; t = ['a', 'b', 'c']
    new_list = s + t            # + creates a new list: [2, 3, 5, 7, 'a', 'b', 'c']
    s.extend(t)                 # extend method modifies the original list
    
  • Lists are a lot like strings.
    • +, index(s[2]), slicing(s[2:5]), in, replication (s * n), ...
    • However, lists are mutable, while strings are immutable (read-only).
OIM3600

List Iteration

# loop through a list using index
for i in range(len(stocks)):
    print(stocks[i])

# loop directly over the items in a list - a more concise and Pythonic way
for stock in stocks:
    print(stock)

# loop through both the index and item of a list simultaneously
for i, stock in enumerate(stocks):
    print(f"Index at {i}: {stock}")
OIM3600

List Item Removal

  • You can remove an item either by element value or by index
    # Using the value
    stocks.remove('AMZN')
    
    # Using the index
    del stocks[1]
    
  • The pop method removes the last item from a list and returns it.
    stocks = ['AAPL', 'MSFT', 'GOOG', 'AMZN']
    last_item = stocks.pop()
    
    print(last_item)                # AMZN
    print(stocks)                   # ['AAPL', 'MSFT', 'GOOG']
    # useful when you want to both remove and retrieve an item from the list.
    
OIM3600

List Search and Sort

  • Search
    stocks = ["AAPL", "MSFT", "GOOG", "AMZN"]
    stocks.index('MSFT')                # 1
    stocks.index('BABA')                # ValueError
    
  • Sort
    s = [10, 1, 7, 3]
    s.sort()                            # [1, 3, 7, 10]
    
    # Use sorted() if you'd like to make a new list instead
    sorted_stocks = sorted(stocks)      # ['AAPL', 'AMZN', 'GOOG', 'MSFT']
    
OIM3600

Exercise: Lists

  1. Create a list symbols from a string symbols = 'AAPL, MSFT, GOOG, AMZN'
  2. symbols[0], symbols[-1], ...
  3. Modify symbols to make it become ["AAPL", "MSFT", "GOOG", "AMZN", 'TSLA']?
  4. 'GOOG' in symbols? 'AA' in symbols?
  5. symbols.lower()? symbols.find('MSFT')?
  6. Sort symbols in reverse?
  7. Convert symbols back to a string?
OIM3600

File Management

  • Open a file.
    f = open('foo.txt', 'r')     # Open for reading (text)
    g = open('bar.txt', 'w')     # Open for writing (text)
    
  • Read all of the data.
    data = f.read()
    
  • Write some text.
    g.write('some text')
    
  • Close when you are done.
    f.close()
    g.close()
    
OIM3600

Common Practices for Reading File Data

  • Read an entire file all at once as a string.
    with open('foo.txt', 'rt') as file:
        data = file.read()
        # data is a string with all the text in foo.txt
    
    • Using with as a context manager automatically closes the file when control leaves the indented code block.
  • Read a file line-by-line by iterating.
    with open(filename, 'rt') as file:
        for line in file:
            ... # Process the line
    
OIM3600

Common Practices for Writing to a File

  • Write string data.
    with open('outfile.txt', 'w') as out:
        out.write('Hello World\n')
    
  • Similarly, you can write CSV data using csv Library
    import csv
    
    data = [['Stock', 'Share'], ['MSFT', 25], ['GOOG', 30]]
    
    with open('data/data.csv', 'w', newline="") as csv_file:
        csv_writer = csv.writer(csv_file)
        # csv_writer.writerows(data) # or 
        for row in data:
            csv_writer.writerow(row)
    
OIM3600

Exercise: Portfolio Calculation

  • The columns in portfolio.csv correspond to the stock name, number of shares, and purchase price of a single stock holding.
  • Create a program called portfolio_cost.py that opens portfolio.csv, reads all lines, and calculates how much it costs to purchase all of the shares in the portfolio. Your program should print output as the following:
    name     shares     price
    AAPL        100    173.93
    MSFT         50    319.53
    GOOG         80    131.36
    AMZN        200    129.33
    NVDA         20    410.17
    TSLA        150    255.70
    Total cost: $116302.70
    
OIM3600

Data Structures

  • Real programs have more complex data.
    • Example: information about a stock holding "100 shares of GOOG at $131.36"
    • This is an "object" with three parts:
      • Name or symbol of the stock ("GOOG", a string)
      • Number of shares (100, an integer)
      • Price (131.36, a float)
  • Data Structures in (Python) programming: collections that can hold multiple data items, such as lists, tuples, dictionaries, sets
    • Note: Data structures in Python are distinct from the data structures covered in Data Structures & Algorithms (DS&A) course/sessions.
OIM3600

Tuples

  • A tuple is a collection of values grouped together.
  • Example
    s = ('GOOG', 100, 131.36)    
    s = 'GOOG', 100, 131.36             # You can ommit (), but not recommended.
    
  • Special cases (0-tuple, 1-tuple).
    t = ()                              # An empty tuple
    w = ('GOOG', )                      # A 1-item tuple
    
  • Tuples are often used to represent simple records or structures.
    • Typically, it is a single object of multiple parts.
    • A good analogy: A tuple is like a single row in a database table.
OIM3600

Tuples vs. Lists

  • Tuples look like read-only lists.
    stock = ('GOOG', 100, 131.36)
    name = stock[0]                         # 'GOOG'
    shares = stock[1]                       # 100
    price = stock[2]                        # 131.36
    
  • However, the contents can't be modified.
    >>> stock[1] = 75
    TypeError: object does not support item assignment
    
OIM3600

Tuple Unpacking

  • Unpacking allows you to extract tuple components into variables for further use.
    stock = ('GOOG', 100, 131.36)
    name, shares, price = stock
    print('Total cost:', shares * price)
    
  • The number of variables on the left must match the tuple structure.
    name, shares = stock            # ValueError: too many values to unpack
    
    # You can use *
    name, *_ = stock                # if you only care about name part
    print(name)                     # GOOG
    
OIM3600

Dictionaries

  • A dictionary is mapping of keys to values.
    s = {}                              # Create an empty dict
    stock = {
        'name': 'GOOG',
        'shares': 100,
        'price': 131.36,
    }
    
    • It's also sometimes called a hash table or hash map.
  • Why dict?
    # stock as a dict
    stock['price']                      # 131.36
    # vs stock as a list
    stock[2]                            # 131.36
    
    • Keys act as indices to access values, but in a more readable way.
OIM3600

Dictionary Common Operations

  • To add or modify values assign using the key names
    • stock['year'] = '2024'
    • stock['shares'] = 75
  • To delete a value use the del statement
    • del s['year]
  • Iterations
    for k in stock.keys():                  # iterate over keys
        print(k)
    
    for v in stock.values():                # iterate over values
        print(v)
    
    for k, v in stock.items():              # iterate over both
        print(f'{k}:{v}')
    
OIM3600

Dictionary Lookups

  • You can test the existence of a key in a dictionary.
    counter = {}
    gene_sequence = 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCT'
    for base in gene_sequence:
        if base in counter:                     # when the key does exist
            counter[base] += 1
        else:                                   # when the key does not exist
            counter[base] = 1
    
  • You can look up a value and provide a default value in case the key doesn't exist.
    for base in gene_sequence:
        counter[base] = counter.get(base, 0) + 1
        # .get(key, 0) means if the key doesn't exist, default value is 0
    
OIM3600

Dictionary Keys

  • In Python, dictionary keys can be of almost any type, but they must be immutable.
  • Example using tuples as keys:
    holidays = {
        (1, 1) : 'New Years',
        (3, 14) : 'Pi day',
        (9, 13) : "Programmer's day",
    }
    
  • Mutable types like list, set, and dict cannot be used as dictionary keys.
    • This is because dictionary keys must remain constant to ensure data integrity.
OIM3600

Sets

  • Sets are collection of unordered unique items.
    stocks = {'TSLA', 'AAPL', 'MSFT'}
    
  • Sets are useful for membership tests, offering faster performance compared to lists.
    print('MSFT' in stocks)                    # True
    print('IBM' in stocks)                     # False
    
  • Sets are also useful for duplicate elimination.
    stocks = ['MSFT', 'AAPL', 'GOOG', 'MSFT', 'GOOG', 'TSLA', 'MSFT']
    
    unique_stocks = set(stocks)                 # {'AAPL', 'GOOG', 'MSFT', 'TSLA'}
    print(len(unique_stocks))                   # 4
    
OIM3600

Set Common Operations

unique_stocks.add('NVDA')                       # Add an item
unique.remove('TSLA')                           # Remove an item

s1 = {'a', 'b', 'c'}
s2 = {'c', 'd'}

s1 | s2                                         # Set union {'a', 'b', 'c', 'd'}
s1 & s2                                         # Set intersection {'c'}
s1 - s2                                         # Set difference {'a', 'b'}

{'b', 'c'}.issubset(s1)                         # True
OIM3600

Choosing Data Structures

  • Programs often have to work with many objects of various types. For example:
    • A portfolio of stocks
    • A table of stock prices
    • A collection of customer orders
  • Main choices:
    • list: ordered data, access by index
    • tuple: ordered data, immutable, access by index
    • dict: (normally treated as) unordered data, access by key
    • set: unordered collection of unique items
OIM3600

Considerations when Choosing Data Structures

  • Data order:
    • Do you need to maintain the order of elements?
  • Mutability:
    • Do you need it to be mutable (changeable) or immutable (fixed)?
  • Fast access:
    • Do you need fast access to elements by key or index?
  • Uniqueness:
    • Do you need to ensure that elements are unique within the collection?
OIM3600

Exercise: Choosing Data Structures

OIM3600