Set Comprehensions & Dictionary Comprehensions¶
Set Comprehensions¶
So we’ve learned about list comprehensions and generator expressions. What if we want to use a comprehension but create a set
?
We could do this:
>>> words = ["apple", "orange", "lime", "lemon"]
>>> first_letters = set(w[0] for w in words)
>>> first_letters
set(['o', 'a', 'l'])
Here we’ve used a generator expression and the set constructor to make a set.
But Python actually has a special syntax just for making set comprehensions:
>>> first_letters = {w[0] for w in words}
>>> first_letters
set(['o', 'a', 'l'])
We use curly braces when making a set comprehension because we use curly braces when making a set.
Remembering our code for printing reversible words from a dictionary, there’s one more improvement we can make with what we just learned. We can turn the reversed_words
set into a set comprehension.
Here’s where we left off:
with open('dictionary.txt') as dictionary_file:
words = (
line.rstrip()
for line in dictionary_file
)
words_over_five_letters = [
word
for word in words
if len(word) > 5
]
# Store the reverse of all long words
reversed_words = set()
for word in words_over_five_letters:
reversed_words.add(word[::-1])
reversible_words = [
word
for word in words_over_five_letters
if word in reversed_words
]
for word in reversible_words:
print(word)
We can take the for loop that adds to the reversed_words
set and turn into a set comprehension:
reversed_words = {
word[::-1]
for word in words_over_five_letters
}
Here’s our final result:
with open('dictionary.txt') as dictionary_file:
words = (
line.rstrip()
for line in dictionary_file
)
words_over_five_letters = [
word
for word in words
if len(word) > 5
]
reversed_words = {
word[::-1]
for word in words_over_five_letters
}
reversible_words = [
word
for word in words_over_five_letters
if word in reversed_words
]
for word in reversible_words:
print(word)
Great! We’ve taken all but one for loop we started with and turned them into list comprehensions. Then we took two and turned them into space- and time-efficient generators.
Dict Comprehensions¶
What about making a dictionary from a generator expression?
Let’s say we have a dictionary where the values are numbers and we want to make a new dictionary which has only values with two digit numbers.
>>> favorite_numbers = {'rebecca': 293, 'ronald': 76, 'dorothy': 62, 'harold': 36, 'matt': 314}
>>> dict((k, v) for k, v in favorite_numbers.items() if v < 100)
{'ronald': 76, 'harold': 36, 'dorothy': 62}
Here we’re using a generator expression and a dict
constructor to make a dictionary.
Python has a special syntax for creating dictionary comprehensions that we should use instead:
>>> {k: v for k, v in favorite_numbers.items() if v < 100}
{'ronald': 76, 'harold': 36, 'dorothy': 62}
Notice the k: v
syntax which is very similar to the key-value syntax used for defining dictionaries.
Let’s create a dictionary where the keys are letters and the values are the indexes of those letters (where a is 1 and z is 26):
>>> from string import ascii_lowercase
>>> letters = {letter: n + 1 for n, letter in enumerate(ascii_lowercase)}
>>> letters['t']
20
>>> word = "Trey"
>>> encoded_word = [letters[x] for x in word.lower()]
>>> encoded_word
[20, 18, 5, 25]
More Comprehension Exercises¶
These exercises are all in the more.py
file in the exercises
directory. Edit the file to add the functions or fix the error(s) in the existing function(s). To run the test: from the exercises
folder, type python test.py <function_name>
, like this:
$ python test.py flip_dict
Flipped Dictionary¶
Edit the function flip_dict
, that flips dictionary keys and values.
Example usage:
>>> from more import flip_dict
>>> flip_dict({'Python': "2015-09-15", 'Java': "2015-09-14", 'C': "2015-09-13"})
{'2015-09-13': 'C', '2015-09-15': 'Python', '2015-09-14': 'Java'}
ASCII Strings¶
Edit the function get_ascii_codes
so that it accepts a list of strings and returns a dictionary containing the strings as keys and a list of corresponding ASCII character codes as values.
>>> from more import get_ascii_codes
>>> words = ["hello", "bye", "yes", "no", "python"]
>>> get_ascii_codes(words)
{'yes': [121, 101, 115], 'hello': [104, 101, 108, 108, 111], 'python': [112, 121, 116, 104, 111, 110], 'no': [110, 111], 'bye': [98, 121, 101]}
Double-valued Dictionary¶
Edit the function dict_from_truple
so that it accepts a list of three-item tuples and returns a dictionary where the keys are the first item of each tuple and the values are a two-tuple of the remaining two items of each input tuple.
Example usage:
>>> from more import dict_from_truple
>>> dict_from_truple([(1, 2, 3), (4, 5, 6), (7, 8, 9)])
{1: (2, 3), 4: (5, 6), 7: (8, 9)}
Multi-valued Dictionary¶
Edit the function dict_from_tuple
by starting with the code from your dict_from_truple
function, above, and modify it to accept a list of tuples of any length and return a dictionary which uses the first item of each tuple as keys and all subsequent items as values.
Example usage:
>>> from more import dict_from_tuple
>>> dict_from_tuple([(1, 2, 3, 4), (5, 6, 7, 8)])
{1: (2, 3, 4), 5: (6, 7, 8)}
>>> dict_from_tuple([(1, 2, 3), (4, 5, 6), (7, 8, 9)])
{1: (2, 3), 4: (5, 6), 7: (8, 9)}
Factors¶
Edit the function get_all_factors
so that it takes a set of numbers and makes a dictionary containing the numbers as keys and a list of factors as values.
>>> from more import get_all_factors
>>> get_all_factors({1, 2, 3, 4})
{1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 2, 4]}
>>> get_all_factors({62, 293, 314})
{314: [1, 2, 157, 314], 293: [1, 293], 62: [1, 2, 31, 62]}
Hint
You can use this function to find the factors of any number:
def get_factors(number):
"""Get factors of the given number."""
return [
n
for n in range(1, number + 1)
if number % n == 0
]