Containers¶
Containers are a data type that contains other objects.
Lists¶
Python's basic container type is the list
We can define our own list with square brackets:
[1, 3, 7]
type([1, 3, 7])
Lists do not have to contain just one type:
various_things = [1, 2, "banana", 3.4, [1, 2]]
We access an element of a list with an int
in square brackets:
one = 1
two = 2
three = 3
my_new_list = [one, two, three]
middle_value_in_list = my_new_list[1]
middle_value_in_list
[1, 2, 3][1]
various_things[2]
index = 2
various_things[index]
Note that list indices start from zero.
We can quickly make a list containing a range of consecutive integer numbers using the built-in range
function
count_to_five = list(range(5))
print(count_to_five)
We can use a string to join together a list of strings:
name = ["Grace", "Brewster", "Murray", "Hopper"]
print(" ".join(name))
And we can split up a string into a list:
"Ernst Stavro Blofeld".split(" ")
We can an item to a list:
name.append("BA")
print(" ".join(name))
Or we can add more than one:
name.extend(["MS", "PhD"])
print(" ".join(name))
Or insert values at different points in the list
name.insert(0, "Admiral")
print(" ".join(name))
Sequences¶
Many other things can be treated like lists
. Python calls things that can be treated like lists sequences.
A string is one such sequence type
print(count_to_five[1])
print("James"[2])
print(count_to_five[1:3])
print("Hello World"[4:8])
print(len(various_things))
print(len("Python"))
len([[1, 2], 4])
Unpacking¶
Multiple values can be unpacked when assigning from sequences, like dealing out decks of cards.
mylist = ["Goodbye", "Cruel"]
a, b = mylist
print(a)
a = mylist[0]
b = mylist[1]
Checking for containment¶
The list
we saw is a container type: its purpose is to hold other objects. We can ask python whether or not a
container contains a particular item:
"Dog" in ["Cat", "Dog", "Horse"]
"Bird" in ["Cat", "Dog", "Horse"]
2 in range(5)
99 in range(5)
"a" in "cat"
Mutability¶
An list can be modified:
name = "Grace Brewster Murray Hopper".split(" ")
print(name)
name[0:3] = ["Admiral"]
name.append("PhD")
print(" ".join(name))
Tuples¶
A tuple
is an immutable sequence:
my_tuple = ("Hello", "World")
my_tuple
my_tuple[0] = "Goodbye"
str
is immutable too:
fish = "Hake"
fish[0] = "R"
But note that container reassignment is moving a label, not changing an element:
fish = "Rake" ## OK!
Supplementary material: Try the online memory visualiser for this one.
Memory and containers¶
The way memory works with containers can be important:
x = list(range(3))
print(x)
y = x
print(y)
z = x[0:3]
y[1] = "Gotcha!"
print(x)
print(y)
print(z)
z[2] = "Really?"
print(x)
print(y)
print(z)
Supplementary material: This one works well at the memory visualiser.
x = ["What's", "Going", "On?"]
y = x
z = x[0:3]
y[1] = "Gotcha!"
z[2] = "Really?"
x
The explanation: While y
is a second label on the same object, z
is a separate object with the same data.
Nested objects make it even more complicated:
x = [["a", "b"], "c"]
y = x
z = x[0:2]
x[0][1] = "d"
z[1] = "e"
x
y
z
Try the visualiser again.
Identity versus equality¶
Having the same data is different from being the same actual object in memory:
print([1, 2] == [1, 2])
print([1, 2] is [1, 2])
The ==
operator checks, element by element, that two containers have the same data.
The is
operator checks that they are actually the same object.
my3numbers = list(range(3))
print(my3numbers)
[0, 1, 2] == my3numbers
[0, 1, 2] is my3numbers
But, and this point is really subtle, for immutables, the Python language might save memory by reusing a single instantiated copy. This will always be safe.
word = "Hello"
print("Hello" == word)
print("Hello" is word)