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!
- Lists
- Tuples
- Sets
- Dictionaries
- Parameter Passing
- Handling Errors in Python
- Modules
- Functional Influences in Python
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 asstr[0:b]
- when the second param is empty, End is assumed:
str[a:]
<->str[a:End+1]
- when first param is empty, 0 is assumed:
- negative indices: represent relative to the end of the string
str[-1]
is the last character in strstr[:-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
andnot 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
.
- It’s idiomatic to name this parameter
- 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/