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)
Close