try
/except
format
)collections
map
...or: a day of shortcuts
Note: Shortcuts make your life easier, but your code less understandable to beginners.
It's hard to search for things you don't know exist.
Today's goal : let you know these things exist.
NOT today's goal : teach you all the details.
You do not need to incorporate these into your project, but if they'll help you, go for it!
import antigravity
try
/except
¶try
code block; only go to except
if try
raises an error# Note: `x` has not been previously defined
try:
print(x)
except:
print("Error encountered")
Different error types can be handled differently:
try:
print(x)
except NameError:
print("NameError: Check that variable exists")
except IndexError:
print("IndexError: Check that value being indexed exists")
except:
print("Error: Error found; check code")
Use the format
method to manipulate and format strings.
name = "Shannon"
job = "Associate Teaching Professor"
topic = "Python"
"Hello! My name is {}. My job is {}, and I work on {}".format(name, job, topic)
Sets are a variable type that store only unique entries.
my_set = set([1, 1, 2, 3, 4])
my_set
Like lists, they are iterable & mutable.
my_set.add(6)
my_set
The collections
module has a number of useful variable types, with special properties.
"Special editions" of collections we've discussed before (lists, tuples, and dictionaries).
from collections import Counter
# count how many elements there are in collection
# return values in a dictionary
Counter([1, 0, 1, 2, 1])
import collections
collections.Counter([1, 0, 1, 2, 1])
# can count how many of each letter are in there
Counter("I wonder how many times I use the letter 'e'")
Think back to our encryption scheme!
The most common letter in the English language is 'e'.
If you just move each letter over by 1 position, you can just use a Counter to crack the code.
Why we moved to a variable encoder!
args
& kwargs
¶*args
- allow any number of extra arguments (including zero)**kwargs
- allow any number of keyword arguments to be passed (as a dictionary)*args
¶# use *arguments to demonstrate args
def tell_audience(*arguments):
for arg in arguments:
print(arg)
tell_audience('asdf')
tell_audience("Hello!",
"My name is Shannon.",
"My job is Associate Teaching Professor.")
**kwargs
¶def tell_audience_kwargs(**info):
print("type: ", type(info), '\n')
for key, value in info.items():
print("key: ", key)
print("value: ", value, "\n")
tell_audience_kwargs(first='Shannon',
last='Ellis',
email='sellis@ucsd.edu')
List comprehensions allow you to run loops and conditionals, inside lists.
The general format is :
[ expression for item in list if conditional ]
# NOT a list comprehension
# how we've been doing it
input_list = [0, 1, 2]
output_list = []
for ind in input_list:
output_list.append(ind + 1)
# look at output
output_list
# This list comprehension is equivalent to the cell above
# note the square brackets on the outside
[ind + 1 for ind in [0, 1, 2]]
# You can also include conditionals inside a list comprehension
[val for val in [1, 2, 3, 4, 5] if val % 2 == 0]
Note: there are also tuple & dictionary comprehensions. They're just used less frequently.
Regular expressions allow you to work with specified patterns in strings.
import re
my_string = "If 12 Python programmers try to complete 14 tasks in 16 minutes, why?"
# can just search for a letter
re.findall('o', my_string)
# but the patterns are where these shine
re.findall('\d\d', my_string)
Lambda functions are small, anonymous functions.
Can define them in line.
increment = lambda a: a + 1
increment(1)
# not a lambda function
def lambda_equivalent(a):
output = a + 1
return output
lambda_equivalent(1)
map
applies a given function to each element in a collection.
# create a function
def double(val):
return val * 2
# map function to each element of list
my_list = [1, 2, 3, 4]
list(map(double, my_list))
# Note - we can use lambda functions with map
list(map(lambda x: x *2, my_list))
Inheritance allows us to define a class that inherits all the methods and attributes from another class. Parent class (aka super class aka base class) is the class being inherited from. Child class (aka derived class) is the class getting all the parent's hard work for free :)
Now we can start to design code with efficient reuse of work we already did!
class Person:
def __init__(self, fname, lname):
self.firstname = fname
self.lastname = lname
def get_first_last(self):
return self.prefix + ' ' + self.firstname+ ' ' + self.lastname
def get_last_first(self):
return self.lastname + ', ' + self.firstname
p1 = Person('Shannon', 'Ellis')
print(p1.get_first_last())
print(p1.get_last_first())
Let's make a Student class...
but I'm lazy and don't want to do all the work of reimplementing. So let's inherit Person's abilities
# as is this will cause an error in the next cell execution
# but if you add (Person) at the end of `class Student`
# then we inherit the properties, no error!
class Student: # class Student(Person):
pass
s1 = Student('Kayden', 'Altman')
print(s1.get_first_last())
print(s1.get_last_first())
Once you add (Person) to inherit all of Person's attributes and methods, class Student is reusing all the work you did with Person!
Lets make Student have extra abilities... so we need to add new instance attributes like PID and grade.
class Student(Person):
def __init__(self, fname, lname, pid, grade):
self.pid = pid
self.grade = grade
s1 = Student('Kayden', 'Altman', 'A12345', 'A-')
print(s1.get_first_last())
print(s1.get_last_first())
When you add the __init__()
function, the child class will no longer inherit the parent's __init__()
function!
To keep the inheritance of the parent's init() function, explicitly add a call to the parent's init() function:
class Student(Person):
def __init__(self, fname, lname, pid, grade):
Person.__init__(self, fname, lname)
self.pid = pid
self.grade = grade
s1 = Student('Kayden', 'Altman', 'A12345', 'A-')
print(s1.get_first_last())
print(s1.get_last_first())
But we don't even have to care what the NAME of the parent class is... python has a generic "get my parent" function called super()
NB: when you call super()
you do NOT have to explicitly pass the self
pointer
class Student(Person):
def __init__(self, fname, lname, pid, grade):
super().__init__(fname, lname)
self.pid = pid
self.grade = grade
s1 = Student('Kayden','Altman','A12345','A-')
print(s1.get_first_last())
print(s1.get_last_first())
Modify the Person class to store a name prefix (like mr., dr. or prof. ). Make get_first_last()
print out the prefix too.
Now redefine Student class to take advantage of that extra work you did in the superclass with just a tiny bit of work!
## EDIT HERE
class Person:
def __init__(self, fname, lname):
self.firstname = fname
self.lastname = lname
def get_first_last(self):
return self.prefix + ' ' + self.firstname+ ' ' + self.lastname
def get_last_first(self):
return self.lastname + ', ' + self.firstname
class Student(Person):
def __init__(self, fname, lname, pid, grade):
super().__init__(fname, lname)
self.pid = pid
self.grade = grade
# try it out
We can implement a GUIs using iPyWidgets
# you may need this
%pip install --user ipywidgets
import ipywidgets as widgets
@widgets.interact
def f(x=5):
print(x*x)
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
@widgets.interact_manual(
color=['blue', 'red', 'green'], lw=(1., 10.))
def plot(freq=1., color='blue', lw=2, grid=True):
t = np.linspace(-1., +1., 1000)
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
ax.plot(t, np.sin(2 * np.pi * freq * t),
lw=lw, color=color)
ax.grid(grid)
To teach you a skill - of how to do things with Python.
You've been more formally trained than many people out in the world programming.
After COGS 18, I feel ____________ my Python programming abilities
After COGS 18, I feel the following about my future Python programming:
This course used:
Thank you to Tom for his original design of this course.
Thank you to the TAs & IAs for their tireless work on this class.
Thank you students for you time, effort and patience.