More on Strings and Lists

In lecture, we learned about variable assignments and basic operations on strings and lists.

As a self-check to see how much you remember, think about what the output of the code below will be, and then run it.

In [1]:
s = "abcde"
l =  [2, 4, 6, 8, 10]
print(s[-2])
print(s[1:3])
print(l[4])
d
bc
10

We'll cover more you can do with strings and lists. Most of these will be methods, like string.split() from lecture, but we will also cover some operators, like +. Some important methods will be covered today that you might've already seen (or will see) in lecture, hopefully the repetition will help you remember them!

Equality with lists and strings

Python has two types of "are these two the same?" operators: == (equality) and is (identity). == checks to see if two expressions have the same value, while is checks to see if they have the same reference.

First, let's start with ==. This checks if two things are equal. With strings, this is pretty intuitive, two strings are equal whenever they have exactly the same characters.

In [2]:
"word" == "word"
Out[2]:
True
In [3]:
"word" == "WORD"
Out[3]:
False

Lists work the same way. Two lists are equal whenever they have the same elements.

In [4]:
["apple", "apple", "apple"] == ["apple", "apple", "apple"]
Out[4]:
True
In [5]:
["apple", "apple", "apple"] == ["orange", "orange", "orange"]
Out[5]:
False

When checking equality, Python doesn't care whether two lists are the same, they just care if they have the same value. This is different from some other programming languages.

In [6]:
a = ["🍎", "🍎", "🍎"] # these are apple (the fruit) emojis if they don't display on your computer
b = ["🍎", "🍎", "🍎"]
print(a == b)
b[0] = "apple"
print(a == b)
True
False

If you want to check whether two lists are have the same reference (that is, they are exactly the same and changing one changes the other), Python has is.

In [7]:
a = [10, 20, 30]
b = [10, 20, 30]
c = a

print(a is b)
print(a is c)
False
True

Because strings are immutable, you don't have to worry about what is does on strings because it won't ever matter.

The in operator

A useful operator for lists and strings (and other data types we will see later) is in. This checks if some value is, well, in some list or string.

Here are some examples:

In [8]:
"crimson" in ["aquamarine", "amber", "crimson"]
Out[8]:
True
In [9]:
"fuschia" in ["aquamarine", "amber", "crimson"]
Out[9]:
False
In [10]:
"J" in "Jupyter"
Out[10]:
True
In [11]:
"j" in "Jupyter"
Out[11]:
False

Similarly, there is not in. This, as you may expect, does the reverse:

In [12]:
"crimson" not in ["aquamarine", "amber", "crimson"]
Out[12]:
False
In [13]:
"fuschia" not in ["aquamarine", "amber", "crimson"]
Out[13]:
True
In [14]:
"J" not in "Jupyter"
Out[14]:
False
In [15]:
"j" not in "Jupyter"
Out[15]:
True

When we are checking strings, Python is checking if the left hand side is included anywhere in the right hand side. So you can check to see if some word is in a given sentence, for example:

In [16]:
"py" in "Jupyter"
Out[16]:
True

This doesn't work with lists:

In [17]:
["amber", "crimson"] in ["aquamarine", "amber", "crimson"]
Out[17]:
False

This is because lists can contain other lists. If you run [1, 2] in [1, 2, 3], Python looks to see if one of the elements of the list is exactly [1, 2].

Some string methods

Here are some methods which can be used to work on strings. You can see these and more here. One thing to remember string methods is because strings are immutable, all string methods leave the original string unchanged. All they do is return a new string with the desired result.

lower and upper:

These commands take a string and put all the letters in lowercase or uppercase, respectively. This is useful for checking if two strings are the same, ignoring case.

In [18]:
"Ucla".lower()
Out[18]:
'ucla'
In [19]:
"Ucla".upper()
Out[19]:
'UCLA'
In [20]:
"Ucla".lower() == "UCLA".lower()
Out[20]:
True

replace:

This command lets you replace substrings within a string with something else.

In [21]:
"confidential information".replace("i","-")
Out[21]:
'conf-dent-al -nformat-on'
In [22]:
"confidential information".replace("confidential", "------")
Out[22]:
'------ information'

join:

This method is the reverse of .split() (from lecture). When you run s.join(l) the output is a string that contains each entry of l (a list) with a copy of s between them. This allows you to take a list and write it as a comma or space-separated string.

In [23]:
' '.join(["Space","seperated","string"])
Out[23]:
'Space seperated string'
In [24]:
", ".join(["Comma","seperated","string"])
Out[24]:
'Comma, seperated, string'

.

.

.

Exercise: Use the above to write code to check to see if a string s contains the letter "q" anywhere? (Uppercase OR lowercase!)

.

.

.

.

.

.

.

.

.

.

.

(see my website here or discussion video for solution)

In [25]:
s = "some string with Q!!"

print('q' in s.lower()) #solution 1
print('Q' in s.upper()) #solution 2
True
True

.

.

.

Exercise: Use the above and the .split() method from lecture to write code that takes a string s and returns a list of every word in s, each word in upper case.

.

.

.

.

.

.

.

.

.

.

.

(see my website here or discussion video for solution)

In [26]:
s.upper().split(" ")
Out[26]:
['SOME', 'STRING', 'WITH', 'Q!!']

Functions and methods for strings and lists

len():

This gives you the size of a list or a string.

In [27]:
len([1, 2, 3, 4])
Out[27]:
4
In [28]:
len("abc")
Out[28]:
3

Remember that indexing works such that the first character has index 0. Therefore if x is a list or a string, the index len(x) points after the last element. This gives you another way to index the end of a list or string:

In [29]:
s = "123456"
print(s[-1], s[len(s)-1])
print(s[3:], s[3:len(s)])
6 6
456 456

index:

This gives you the (first) index of the location of what you're looking for in the string or list. For strings, it checks for substrings, while for lists, it checks for specific elements.

In [30]:
"Well, well, well".index("e")
Out[30]:
1
In [31]:
"Well, well, well".index("ll")
Out[31]:
2
In [32]:
[1, 1, 2, 3, 5, 8, 13].index(2)
Out[32]:
2

count:

This counts the number of times an element or substring appears in a list or string.

In [33]:
"Well, well, well".count("ell")
Out[33]:
3
In [34]:
"Well, well, well".count("Well")
Out[34]:
1
In [35]:
[1, 1, 2, 3, 5, 8, 13].count(1)
Out[35]:
2

List methods and syntax

Unlike strings, lists are mutable. List methods and syntax sometimes can change the original list (but also sometimes they don't!). Whenever you use a list method, make sure you know whether or not you're modifying the original list! All the ones covered so far do not modify the original list. When looking at the Python documentation, any method that says "in-place" modifies the original list.

Unfortunately, there's no one good reference for lists in the Python documentation. This page lists the important methods, but it's not exclusively focused on lists.

pop:

This method returns an element of the list at a given index and removes it from the original list. If you don't give it an index, it returns the last.

In [36]:
l = ["this", "is", "my", "list", ":)"]
print(l.pop())
l
:)
Out[36]:
['this', 'is', 'my', 'list']
In [37]:
print(l.pop(1))
l
is
Out[37]:
['this', 'my', 'list']

remove:

This removes an element of the list. This modifies the original list. It fails if the element is not in the list.

In [38]:
l = ["here is another list!", ":)"]
l.remove(":)")
l
Out[38]:
['here is another list!']

append:

This adds an element to the end of the list. This modifies the original list.

In [39]:
l = ["we", "finish", "each", "other's"]
l.append("lists!")
l
Out[39]:
['we', 'finish', 'each', "other's", 'lists!']

insert:

This also adds an element to a list, but you choose the index it goes in. All elements originally at this index or a later one move up one. As before, this modifies the original list.

It might be tricky to remember where the new element is placed because all the indices will change as a result of the new item. The new element is placed so that it has the index number you give. Every element that was at an earlier index stays in place, while all others move up one to make room.

In [40]:
l = ["one", "three", "four", "five"]
l.insert(1, "two")
l
Out[40]:
['one', 'two', 'three', 'four', 'five']

extend:

Like append, but adds another list to the end (this is called concatenation). The result is the same as if you used +, but it modifies the original list.

In [41]:
l1 = ["here", "is", "the", "start", "of", "the", "list"]
l2 = ["and", "here", "is", "the", "end!"]
l1.extend(l2)
l1
Out[41]:
['here',
 'is',
 'the',
 'start',
 'of',
 'the',
 'list',
 'and',
 'here',
 'is',
 'the',
 'end!']

.

.

.

Exercise: Write code that takes the last element of a list l and places it at the beginning in-place (so modifying the original list). You can assume the list is not empty.

.

.

.

.

.

.

.

.

.

.

.

(see my website here or discussion video for solution)

In [42]:
l = [1, 2, 3]

l.insert(0,l.pop())
l
Out[42]:
[3, 1, 2]

.

.

.

Exercise (trickier than the others): Write code that creates a list l and adds it to itself (this is possible in Python). That is, we want l in l to return True

.

.

.

.

.

.

.

.

.

.

.

(see my website here or discussion video for solution)

In [43]:
l = [1, 2, 3]

l.append(l)
print(l in l)
print(l)
True
[1, 2, 3, [...]]