Skip to main content Link Search Menu Expand Document (external link)

Lecture 06 Continued - python-part3

2022-10-12 | Week 3 | by Ashwin Ranade

Ashwin here! This continues from last lecture, and covers the slides 42-72 on python_intro_v1. These are referencing this video lecture, meant as a continuation of lecture 6.

Table of Contents

Strings!

  • In Python, each string is an object just like our Circle objects!
  • Strings are immutable, meaning they can’t be modified once created.
fact1 = 'Del Taco rules! '
fact2 = 'CS131 is lit! '

truth = fact1 + fact2

truth += 'I have spoken.'
  • The code to the left appears to mutate the string referred to by truth.
  • This line looks like it mutates the object referred to by truth - but in reality, it creates a NEW string object!
    • Then it changes the object reference to point at the new object!
    • At some later time, our original string is garbage collected!
  • Python generates/trashes lots of objects as your code runs – and you don’t even know it!

Substrings

# String slicing in Python
truth = 'UCLA students are awesome!'
print(truth[6]) #t
print(truth[1:3]) #CL
print(truth[:4]) #UCLA
print(truth[22:]) # some!
print(truth[-4:-1]) # ome
  • substring of length 1 from a: str[a]
  • substring from indices [a-b): str[a:b]
    • when first param is empty, 0 is assumed: str[:b] is the same as str[0:b]
    • when the second param is empty, End is assumed: str[a:] <-> str[a:End+1]
  • negative indices: represent relative to the end of the string
    • str[-1] is the last character in str
    • str[:-1] == str[:len(str)-1]

Cool String Functions

  • strip(delimiter) splits a string into an array of strings based on a delimiter (defaults to whitespace)
  • upper(x) returns x all uppercase

Lists

  • Like strings, lists are objects in Python – and they support the same operations as strings!
  • But, lists are mutable!

Assignment still creates a whole new object, despite lists being mutable. For example, stuff = stuff + ['subpar'] creates a new stuff list.

stuff = [42, False, 'walnuts']

stuff[2] = 'USC students'
stuff[0:2] = ["It's", 4, 'real']
stuff.append('are')    # or stuff += ['are']

stuff = stuff + ['subpar']
print(stuff[3:])
if 'walnuts' not in stuff:
  for s in stuff:
    print(s)
------------------------------
['USC students', 'are', 'subpar']
It's
4
real
USC students
are
subpar

List Implementation + Complexity

  • Accessing the jth element of a list, e.g., x[j] is super fast!
  • Lists are implemented with dynamically-allocated arrays of object references.

big-O

  • in and not in: O(n)
  • list[j] where j is large: O(1)
  • appending one list to another: O(m+n)

Lists of Lists

x = [1,2]
lol = [[1],x]
x = [2]
print(x)
-------
[[1],[1,2]]

In x = [2], we re-assign x, and hence it doesn’t affect our original list lol!

Tuples

Python tuples are immutable, ordered groups of items, e.g., ([1,2,3],False), (1,True,'foo'), etc.

Sets

  • sets can only contain unique elements
  • operations: .add, .remove
draining = set()

draining.add('CS131')
draining.add('dating')
draining.add('studying')
draining.remove('CS131')
print(draining)

if 'CS131' not in draining:
  print('Studying for CS131 is NOT draining!')

# Let's create a set from a list...
dinner = ['salad','soup','steak','soup','pie']
dinner_set = set(dinner)
print(f'Unique foods: {dinner_set}')
---------------------------------
{'dating', 'studying'}
Studying for CS131 is NOT draining!
Unique foods: {'soup', 'steak', 'pie', 'salad'}
  • Python sets hold a single unique copy of each value
  • sets are unordered
  • Python uses hash table for sets

more Python set operations

  • - is difference
  • | is union
  • & is intersection

Dictionaries

  • Python has first-class support for dictionaries (maps) – they’re super fast!
  • Note: Python dictionaries are insertion-ordered as of Python 3.7, but sets are still unordered. relevant Campuswire post
  • dictionary holds a single copy of each unique value
attrs = {'cs32':'a weeder', 'cs143':'practical'}

attrs['cs181'] = 'theoretical'
attrs['cs132'] = ['challenging','applied']
attrs['cs181'] = 'fascinating'

if 'cs181' in attrs:
  print(f"CS181 is known to be {attrs['cs181']}")

del attrs['cs181']    # remove key/value

for key, val in attrs.items():
  print(f'Key {key} maps to {val}')
------------------------------------------------
CS181 is known to be fascinating
Key cs32 maps to a weeder
Key cs143 maps to practical
Key cs132 maps to ['challenging', 'applied']

Parameter Passing

  • It has just one approach – and it’s called “pass by object reference.
  • (And it’s identical to pass by pointer in C++)

Every variable in Python is an object reference – it just holds the address of a value!

  • When we call a function, Python just passes the object reference (pointer) to the function!
  • The parameter is also an object reference (since it’s just a variable).

Parameter Passing Practice

Examples of creating a new object (so original object doesn’t change):

def nerdify(s):
 s = 'coding ' + s

i_like = 'parties'
nerdify(i_like)
print(i_like)

def peachify(f):
 f = f + ['peach']

fruits = ['apple', 'cherry']
peachify(fruits)
print(fruits)

def largeify(c):
 c = Circle(10)

unit = Circle(1)
largeify(unit)
print(unit.radius())
-------------------------
parties
['apple', 'cherry']
1

Examples of mutating the original object, so that the original object DOES change:

def peachify2(f):
 f.append('peach')

fruits = ['apple', 'cherry']
peachify2(fruits)
print(fruits)

def largeify2(c):
 c.set_radius(10)

unit = Circle(1)
largeify2(unit)
print(unit.radius())
-------------------------
['apple', 'cherry', 'peach']
10

Keyword Arguments

  • When you define a parameter with two asterisks **, this forces arguments to be passed to the function in a dictionary.
    • It’s idiomatic to name this parameter kwargs.
  • Each parameter name –> value is added to a dictionary, and the dictionary is passed to the function.
def foo(**kwargs):
  for x,y in kwargs.items():
    print(x,"=>",y)
foo("carey"="nachenberg")
-------------------------
carey => nachenberg

Handling Errors in Python

  • When Python encounters an error that it doesn’t know how to handle, it generates a special error called an “exception.”
  • If you don’t add code to “handle” an exception, it will cause the program to terminate.
  • Why? The function that “generated the exception” will immediately return, then the function that called it will immediately return, and so on, until your program exits!

We use try and except to handle errors in Python.

#we can have the except in this function
def div(a, b):
  try:
    temp = a/b
  except:
    return None #invalid result
  return temp

#we could also catch the error here
def main():
  try:
   result = div(10, 0)
   print(f'The result was {result}')
  except:
   print('You divided by zero!')

main() # call main function

Finally, we can even have multiple except blocks, each dealing with a different type of issue!

Modules

  • A script is a .py file that implements a main() function and is meant to run a stand-alone program.
  • Scripts are run from the command line, like this: python3 script_name.py

  • A module is a .py file that implements a set of related classes or functions for use as a library (e.g., for machine learning).
  • Modules are intended to be imported into a python script or other modules to provide needed functionality.

Importing Modules

You can “import” a module and use its function/classes in your programs.

Different ways to import modules:

import math

def hypot(a,b):
  return math.sqrt(a**2+b**2)
from math import sqrt, cos, sin

def hypot(a,b):
  return sqrt(a**2+b**2)
from math import *

def hypot(a,b):
  return sqrt(a**2+b**2)
import math as m

def hypot(a,b):
  return m.sqrt(a**2+b**2)
  • Each module implicitly defines its own namespace (e.g., math).
  • This prevents collisions between similarly-named functions/classes in different modules.

Creating a Module

our_shape.py

class Circle:
 def __init__(self, radius):
  self.radius = radius

 def area(self):
  return 3.14 * self.radius**2

We import the module with import our_shape, since the file is called our_shape.py. Then, we can create objects using the module’s name as a prefix. c = our_shape.Circle(1)

  • We create larger Python programs using multiple modules

Module versus Script

  • A Python script is a .py file that’s run from the command line.
  • A Python module is a .py file that’s imported by another .py file.

How can we tell?

If you run a .py file from the command line, Python sets __name__ to __main__ indicating it’s a script. Otherwise, Python sets __name__ to the module’s name.

Functional Influences in Python

Comprehensions

List Comprehension:

input = [10,11,12,15,17,22,23,5]
doubled_odds = [x*2 for x in input if x % 2 == 1]

s = "David's dirty dog drank dirty water down by the dam"

Set Comprehension:

wordz3 = {w for w in s.split() if w[0] == 'd'} # hint: set

Dict Comprehension: wordz4 = {w:len(w) for w in s.split()} # hint: dict

Output:

[9, 16, 25]
{'drank', 'dam', 'dirty', 'down', 'dog'}
{"David's": 7, 'dirty': 5, 'dog': 3, 'drank': 5, 'water': 5, 'down': 4, 'by': 2, 'the': 3, 'dam': 3}

Lambdas

def foo(f):
  print(f("Carey"))

def main():
  foo(lambda x : x + "has earwax") #returns "Carey has earwax"
  y = "a lot"
  foo(lambda x : x + "has earwax" + y) #returns "Carey has earwax a lot"
  • The last lambda captures the y variable from the enclosing scope!

Map, Filter, Reduce

Map: The map function returns a result object where f() has been applied to each of the original items. input:

  • A function f() that accepts a single argument and returns f(arg).
  • An iterable object of items – like a list, set, dictionary, etc…
print(map(lambda n : n**2, [1,2,3]))
-------------------------
[2, 4, 9]

Filter: The filter function returns a result object that includes all original items for which f() returned True. input:

  • A predicate function f() that accepts a single argument and returns True/False.
  • An iterable object of items – like a list, set, dictionary, etc…
print(filter(lambda n: n % 2 == 0, [1,2,3,4]))
-------------------------
[2,4]

Reduce: The reduce function returns a single result that is the final accumulated value. Requires import functools Can either take in 2 or 3 inputs:

  • f(accum, item) takes in 2 arguments and returns a new accumulated value
  • iterable object of items
  • optional starting accumulated value (usually zero, empty string, empty list)
    • this starting value is placed before the second argument, if present; otherwise, it will start from the first element of the iterable object
from functools import reduce
adjs = ['nerdy', 'geeky', 'silly']
result = reduce(lambda x,y : y + ' ' + x, adjs)
print(result) #silly geeky nerdy

Python Cheat Sheets for those interested: https://ehmatthes.github.io/pcc_2e/cheat_sheets/cheat_sheets/