6. Strings and Dictionaries
This lesson will be a double-shot of essential Python types: strings and dictionaries.
# Strings
One place where the Python language really shines is in the manipulation of strings. This section will cover some of Python's built-in string methods and formatting operations.
Such string manipulation patterns come up often in the context of data science work, and is one big perk of Python in this context.
# String syntax
You've already seen plenty of strings in examples during the previous lessons, but just to recap, strings in Python can be defined using either single or double quotations. They are functionally equivalent.
x = 'Pluto is a planet'
y = "Pluto is a planet"
x == y
2
3
Double quotes are convenient if your string contains a single quote character (e.g. representing an apostrophe).
Similarly, it's easy to create a string that contains double-quotes if you wrap it in single quotes:
print("Pluto's a planet!")
print('My dog is named "Pluto"')
2
If we try to put a single quote character inside a single-quoted string, Python gets confused:
'Pluto's a planet!' # SyntaxError: invalid syntax
We can fix this by "escaping" the single quote with a backslash.
'Pluto\'s a planet!'
The table below summarizes some important uses of the backslash character.
What you type... | What you get | example | print(example) |
---|---|---|---|
\' | ' | 'What\'s up?' | What's up? |
\" | " | "That's \"cool\"" | That's "cool" |
\\ | \ | "Look, a mountain: /\\" | Look, a mountain: /\ |
\n | "1\n2 3" | 1 2 3 |
The last sequence, \n
, represents the newline character. It causes Python to start a new line.
hello = "hello\nworld"
print(hello)
2
In addition, Python's triple quote syntax for strings lets us include newlines literally (i.e. by just hitting 'Enter' on our keyboard, rather than using the special '\n' sequence). We've already seen this in the docstrings we use to document our functions, but we can use them anywhere we want to define a string.
triplequoted_hello = """hello
world"""
print(triplequoted_hello)
triplequoted_hello == hello
2
3
4
The print()
function automatically adds a newline character unless we specify a value for the keyword argument end
other than the default value of '\n'
:
print("hello")
print("world")
print("hello", end='')
print("pluto", end='')
2
3
4
# Strings are sequences
Strings can be thought of as sequences of characters. Almost everything we've seen that we can do to a list, we can also do to a string.
# Indexing
planet = 'Pluto'
planet[0]
2
3
# Slicing
planet[-3:]
2
# How long is this string?
len(planet)
2
# Yes, we can even loop over them
[char+'! ' for char in planet]
2
But a major way in which they differ from lists is that they are immutable. We can't modify them.
planet[0] = 'B'
# planet.append doesn't work either
2
# String methods
Like list
, the type str
has lots of very useful methods. I'll show just a few examples here.
# ALL CAPS
claim = "Pluto is a planet!"
claim.upper()
2
3
# all lowercase
claim.lower()
2
# Searching for the first index of a substring
claim.index('plan')
2
# planet = 'Pluto'
claim.startswith(planet)
2
claim.endswith('dwarf planet')
# Going between strings and lists: .split()
and .join()
str.split()
turns a string into a list of smaller strings, breaking on whitespace by default. This is super useful for taking you from one big string to a list of words.
words = claim.split()
words
2
Occasionally you'll want to split on something other than whitespace:
datestr = '1956-01-31'
year, month, day = datestr.split('-')
2
str.join()
takes us in the other direction, sewing a list of strings up into one long string, using the string it was called on as a separator.
'/'.join([month, day, year])
# Yes, we can put unicode characters right in our string literals :)
' 👏 '.join([word.upper() for word in words])
2
# Building strings with .format()
Python lets us concatenate strings with the +
operator.
planet + ', we miss you.'
If we want to throw in any non-string objects, we have to be careful to call str()
on them first
position = 9
planet + ", you'll always be the " + position + "th planet to me." # TypeError: must be str, not int
2
planet + ", you'll always be the " + str(position) + "th planet to me."
This is getting hard to read and annoying to type. str.format()
to the rescue.
"{}, you'll always be the {}th planet to me.".format(planet, position)
So much cleaner! We call .format()
on a "format string", where the Python values we want to insert are represented with {}
placeholders.
Notice how we didn't even have to call str()
to convert position
from an int. format()
takes care of that for us.
If that was all that format()
did, it would still be incredibly useful. But as it turns out, it can do a lot more. Here's just a taste:
pluto_mass = 1.303 * 10**22
earth_mass = 5.9722 * 10**24
population = 52910390
# 2 decimal points 3 decimal points, format as percent separate with commas
"{} weighs about {:.2} kilograms ({:.3%} of Earth's mass). It is home to {:,} Plutonians.".format(
planet, pluto_mass, pluto_mass / earth_mass, population,
)
2
3
4
5
6
7
# Referring to format() arguments by index, starting from 0
s = """Pluto's a {0}.
No, it's a {1}.
{0}!
{1}!""".format('planet', 'dwarf planet')
print(s)
2
3
4
5
6
You could probably write a short book just on str.format
, so I'll stop here, and point you to pyformat.info (opens new window) and the official docs (opens new window) for further reading.
# Dictionaries
Dictionaries are a built-in Python data structure for mapping keys to values.
numbers = {'one':1, 'two':2, 'three':3}
In this case 'one'
, 'two'
, and 'three'
are the keys, and 1, 2 and 3 are their corresponding values.
Values are accessed via square bracket syntax similar to indexing into lists and strings.
numbers['one']
We can use the same syntax to add another key, value pair
numbers['eleven'] = 11
numbers
2
Or to change the value associated with an existing key
numbers['one'] = 'Pluto'
numbers
2
Python has dictionary comprehensions with a syntax similar to the list comprehensions we saw in the previous tutorial.
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
planet_to_initial = {planet: planet[0] for planet in planets}
planet_to_initial
2
3
The in
operator tells us whether something is a key in the dictionary
'Saturn' in planet_to_initial
'Betelgeuse' in planet_to_initial
A for loop over a dictionary will loop over its keys
for k in numbers:
print("{} = {}".format(k, numbers[k]))
2
We can access a collection of all the keys or all the values with dict.keys()
and dict.values()
, respectively.
# Get all the initials, sort them alphabetically, and put them in a space-separated string.
' '.join(sorted(planet_to_initial.values()))
2
The very useful dict.items()
method lets us iterate over the keys and values of a dictionary simultaneously. (In Python jargon, an item refers to a key, value pair)
# str.rjust(10) 字符串占10位,右对齐
for planet, initial in planet_to_initial.items():
print("{} begins with \"{}\"".format(planet.rjust(10), initial))
2
3
To read a full inventory of dictionaries' methods, click the "output" button below to read the full help page, or check out the official online documentation (opens new window).
# Exercise: Strings and Dictionaries
You've learned a lot of Python... go test your skills (opens new window) with some realistic programming applications.
# 0.
Let's start with a string lightning round to warm up. What are the lengths of the strings below?
For each of the five strings below, predict what len()
would return when passed that string. Use the variable length
to record your answer, then run the cell to check whether you were right.
a = ""
length = 0
q0.a.check()
2
3
Correct:
The empty string has length zero. Note that the empty string is also the only string that Python considers as False when converting to boolean.
b = "it's ok"
length = 7
q0.b.check()
2
3
Correct:
Keep in mind Python includes spaces (and punctuation) when counting string length.
c = 'it\'s ok'
length = 7
q0.c.check()
2
3
Correct:
Even though we use different syntax to create it, the string c
is identical to b
. In particular, note that the backslash is not part of the string, so it doesn't contribute to its length.
d = """hey"""
length = 3
q0.d.check()
2
3
Correct:
The fact that this string was created using triple-quote syntax doesn't make any difference in terms of its content or length. This string is exactly the same as 'hey'
.
e = '\n'
length = 1
q0.e.check()
2
3
Correct:
The newline character is just a single character! (Even though we represent it to Python using a combination of two characters.)
# 1.
There is a saying that "Data scientists spend 80% of their time cleaning data, and 20% of their time complaining about cleaning data." Let's see if you can write a function to help clean US zip code data. Given a string, it should return whether or not that string represents a valid zip code. For our purposes, a valid zip code is any string consisting of exactly 5 digits.
HINT: str
has a method that will be useful here. Use help(str)
to review a list of string methods.
def is_valid_zip(zip_code):
"""Returns whether the input string is a valid (5 digit) zip code
"""
return len(zip_code) == 5 and zip_code.isdigit()
q1.check()
2
3
4
5
6
# 2.
A researcher has gathered thousands of news articles. But she wants to focus her attention on articles including a specific word. Complete the function below to help her filter her list of articles.
Your function should meet the following criteria
- Do not include documents where the keyword string shows up only as a part of a larger word. For example, if she were looking for the keyword “closed”, you would not include the string “enclosed.”
- She does not want you to distinguish upper case from lower case letters. So the phrase “Closed the case.” would be included when the keyword is “closed”
- Do not let periods or commas affect what is matched. “It is closed.” would be included when the keyword is “closed”. But you can assume there are no other types of punctuation.
def word_search(documents, keyword):
"""
Takes a list of documents (each document is a string) and a keyword.
Returns list of the index values into the original list for all documents
containing the keyword.
Example:
doc_list = ["The Learn Python Challenge Casino.", "They bought a car", "Casinoville"]
>>> word_search(doc_list, 'casino')
>>> [0]
"""
# list to hold the indices of matching documents
indices = []
# Iterate through the indices (i) and elements (doc) of documents
for i, doc in enumerate(doc_list):
# Split the string doc into a list of words (according to whitespace)
tokens = doc.split()
# Make a transformed list where we 'normalize' each word to facilitate matching.
# Periods(句号) and commas(逗号) are removed from the end of each word, and it's set to all lowercase.
normalized = [token.rstrip('.,').lower() for token in tokens]
# Is there a match? If so, update the list of matching indices.
if keyword.lower() in normalized:
indices.append(i)
return indices
q2.check()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
上面官方给出的答案貌似存在问题,比如多重嵌套的list:
lists=[['The Learn Python Challenge Casino', 'They bought a car, and a horse', 'Casinoville?'], 'casino']
print(word_search(lists, 'casino'))
2
# 3.
Now the researcher wants to supply multiple keywords to search for. Complete the function below to help her.
(You're encouraged to use the word_search
function you just wrote when implementing this function. Reusing code in this way makes your programs more robust and readable - and it saves typing!)
def multi_word_search(doc_list, keywords):
"""
Takes list of documents (each document is a string) and a list of keywords.
Returns a dictionary where each key is a keyword, and the value is a list of indices
(from doc_list) of the documents containing that keyword
>>> doc_list = ["The Learn Python Challenge Casino.", "They bought a car and a casino", "Casinoville"]
>>> keywords = ['casino', 'they']
>>> multi_word_search(doc_list, keywords)
{'casino': [0, 1], 'they': [1]}
"""
keyword_to_indices = {}
for keyword in keywords:
keyword_to_indices[keyword] = word_search(doc_list, keyword)
return keyword_to_indices
q3.check()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17