CS 106A
Stanford University
Chris Gregg
zip
functionenumerate
functionOne of the slightly more advanced features of Python is the list comprehension. List comprehensions act a bit like a for loop, and are used to produce a list in a concise way.
A list comprehension consists of brackets containing an expression followed by a for
clause, then zero or more for
or if
clauses.
The result is a new list resulting from evaluating the expression in the
context of the for and if clauses which follow it.
The list comprehension always returns a list as its result. Example:
>>> my_list
[15, 50, 10, 17, 5, 29, 22, 37, 38, 15]
>>> new_list = [2 * x for x in my_list]
>>> print(new_list)
[30, 100, 20, 34, 10, 58, 44, 74, 76, 30]
In this example, the list comprehension produces a new list where each element is twice the original element in the original list. The way this reads is, "multiply 2 by x for every element, x, in my_list"
Example 2:
>>> my_list
[15, 50, 10, 17, 5, 29, 22, 37, 38, 15]
>>> new_list = [x for x in my_list if x < 30]
>>> print(new_list)
[15, 10, 17, 5, 29, 22, 15]
In this example, the list comprehension produces a new list that takes the original element in the original list only if the element is less than 30. The way this reads is, "select x for every element, x, in my_list if x < 30"
Example 3:
>>> my_list
[15, 50, 10, 17, 5, 29, 22, 37, 38, 15]
>>> new_list = [-x for x in my_list]
>>> print(new_list)
[-15, -50, -10, -17, -5, -29, -22, -37, -38, -15]
In this example, the list comprehension negates all values in the original list. The way this reads is, "return -x for every element, x, in my_list"
Let's do the same conversion for Example 2 from before:
>>> def less_than_30(lst):
... new_list = []
... for x in lst:
... if x < 30:
... new_list.append(x)
... return new_list
...
>>> less_than_30(my_list)
[15, 10, 17, 5, 29, 22, 15]
>>> my_list
[15, 50, 10, 17, 5, 29, 22, 37, 38, 15]
>>> new_list = [x for x in my_list if x < 30]
>>> print(new_list)
[15, 10, 17, 5, 29, 22, 15]
You can see that the list comprehension is more concise than the function, while producing the same result.
The function:
>>> my_list
[15, 50, 10, 17, 5, 29, 22, 37, 38, 15]
>>> new_list = [-x for x in my_list]
>>> print(new_list)
[-15, -50, -10, -17, -5, -29, -22, -37, -38, -15]
We can re-write list comprehensions as functions, to see how they behave in more detail:
>>> my_list
[15, 50, 10, 17, 5, 29, 22, 37, 38, 15]
>>> def negate(lst):
... new_list = []
... for x in lst:
... new_list.append(-x)
... return new_list
...
>>> negate(my_list)
[-15, -50, -10, -17, -5, -29, -22, -37, -38, -15]
Open up PyCharm and create a new project called ListComprehensions. Create a new python file called "comprehensions.py".
Create the following program, and fill in the details for each comprehension. We have done the first one for you:
def main():
my_list = [37, 39, 0, 43, 8, -15, 23, 0, -5, 30, -10, -34, 30, -5, 28, 9,
18, -1, 31, -12]
print(my_list)
# create a list called "positives" that contains all the positive values
# in my_list
positives = [x for x in my_list if x > 0]
print(positives)
# create a list called "negatives" that contains all the negative values
# in my_list
negatives =
print(negatives)
# create a list called "triples" that triples all the values of my_list
triples =
print(triples)
# create a list called "odd_negatives" that contains the negative
# value of all the odd values of my_list
odd_negatives =
print(odd_negatives)
if __name__ == "__main__":
main()
zip
functionOne function which would have made your life (too) easy for assignment 4 is the zip
function, which takes multiple lists and produces one item from each list as a tuple each time the next
function is used on the result of the zip
function. Example:
def zip_examples():
list1 = ['a', 'b', 'c', 'd', 'e']
list2 = [10, 20, 30, 40, 50]
for v1, v2 in zip(list1, list2):
print(v1, v2)
a 10
b 20
c 30
d 40
e 50
Output:
The zip
function works for as many lists as you want.
zip
functionHere is the documentation from help(zip)
:
class zip(object)
| zip(*iterables) --> zip object
|
| Return a zip object whose .__next__() method returns a tuple where
| the i-th element comes from the i-th iterable argument. The .__next__()
| method continues until the shortest iterable in the argument sequence
| is exhausted and then it raises StopIteration.
|
In other words: if the lists (or any iterable, like a string) is exhausted, the zipping stops -- the shortest list determines how many tuples we get:
s1 = "a string"
s2 = "second string"
for v1, v2 in zip(s1, s2):
print(v1, v2)
a s
e
s c
t o
r n
i d
n
g s
Output:
zip
functionYou can have as many iterables as you want with zip:
Output:
s1 = "the"
s2 = "cat"
s3 = "the"
s4 = "bat"
s5 = "the"
s6 = "rat"
print(list(zip(s1, s2, s3, s4, s5, s6)))
[('t', 'c', 't', 'b', 't', 'r'), ('h', 'a', 'h', 'a', 'h', 'a'), ('e', 't', 'e', 't', 'e', 't')]
There is another collection that we have not talked about that is an important one: the set. A set is a collection that cannot hold duplicates.
Example:
def set_examples():
my_set = set()
for c in 'Mississippi':
my_set.add(c)
print(my_set)
{'M', 'p', 's', 'i'}
Output:
You can add duplicate elements as many times as you want to a set, but the set only keeps one copy. You can use the in operator to see if an element is in a set (the output of the following is True.
def set_examples():
my_set = set()
for c in 'Mississippi':
my_set.add(c)
print('s' in my_set)
One interesting feature of a set is that you can find the intersection, union, and difference of sets, and also whether a set is a subset or superset (or disjoint, etc.):
first_three = {1, 2, 3}
evens = {2, 4, 6}
wholes = {0, 1, 2, 3, 4, 5, 6, 7}
print(first_three.union(evens))
print(first_three.intersection(evens))
print(first_three.difference(evens))
print(first_three.issubset(wholes))
print(wholes.issuperset(evens))
{1, 2, 3, 4, 6}
{2}
{1, 3}
True
True
Output:
(note: none of the functions above change the value of either set)
In the same way that list comprehensions are used to convert one list into another list, dictionary comprehensions can be used to convert one dictionary into another.
Dictionary comprehensions use the items function of a dictionary, and have the form:
def dictionary_comprehensions():
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# Double each value in the dictionary
double_dict1 = {k: v * 2 for (k, v) in dict1.items()}
print(f"original dict: {dict1}")
print(f"new dict: {double_dict1}")
original dict: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
new dict: {'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
Output:
dict_variable = {key:value for (key,value) in dictonary.items()}
Example:
You can get fancy and modify the keys:
original dict: {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
new dict: {1: 'd', 2: 'e', 0: 'c'}
Output:
d = {'a': 5, 'b': 6, 'c': 7}
d_xkeys = {k + 'x': v for (k, v) in d.items()}
print(d_xkeys)
You do have to be careful: remember that dictionaries cannot have duplicate keys:
d = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
d_mod = {k % 3: v for (k, v) in d.items()}
print(f"original dict: {d}")
print(f"new dict: {d_mod}")
{'ax': 5, 'bx': 6, 'cx': 7}
Output:
If you want to loop through a collection and you want both the index and the value in the collection, you can do that the way we've seen before:
def enumerate_examples():
xyz = ['x', 'y', 'z']
print("old way:")
for i in range(len(xyz)):
value = xyz[i]
print(f"{i}: {value}")
def enumerate_examples():
xyz = ['x', 'y', 'z']
print("using enumerate:")
for i, value in enumerate(xyz):
print(f"{i}: {value}")
Or you can use the enumerate
function, which gets both parts for you:
old way:
0: x
1: y
2: z
new way:
0: x
1: y
2: z
Output:
I would say that I use the enumerate
function in about 30% of all the python programs I write.
Sometimes, your program does not behave the way you want it to, through no real fault of your own. For example, let's say you try to open a file for reading that doesn't exist:
>>> with open("my_missing_file.txt", "r") as f:
... for line in f:
... print(line)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'my_missing_file.txt'
In this case, your program would crash! We can avoid that crash by catching the exception, meaning that we have Python tell us that there has been an error. For example:
We control the message, and we can recover from the error, instead of crashing the program.
>>> try:
... with open("my_missing_file.txt", "r") as f:
... for line in f:
... print(line)
... except:
... print("Something went wrong when trying to open the file!")
...
Something went wrong when trying to open the file!
>>>
When you have a simple except
statement, this will catch any error, which is often not what you want to do. You should try to catch the actual exception that you expect. For example:
>>> try:
... with open("my_missing_file.txt", "r") as f:
... for line in f:
... print(line)
... except FileNotFoundError:
... print("Something went wrong when trying to open the file!")
...
Something went wrong when trying to open the file!
>>> try:
... with open("my_missing_file.txt", "r") as f:
... for line in f:
... print(line)
... except:
... print("Something went wrong when trying to open the file!")
...
Something went wrong when trying to open the file!
>>>
We knew that there could be a FileNotFoundError, so we caught it directly (how did we know? We tried it and saw that error, on the last slide!)
You can catch any error that the system produces, as long as you know what you are looking for. For example:
>>> a = int(input("Please enter an integer: "))
Please enter an integer: October
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'October'
>>> valid_input = False
>>> while not valid_input:
... try:
... a = int(input("Please enter an integer: "))
... valid_input = True
... except ValueError:
... print("That wasn't a valid integer...")
...
Please enter an integer: October
That wasn't a valid integer...
Please enter an integer: Bob
That wasn't a valid integer...
Please enter an integer: -3
In this case, we continued the program, even though the user kept typing non-integer inputs. We saved ourselves from a crash!