List & Tuple: Ordered Array
Python Sequences Are Ordered Arrays¶
Python Sequences come in two flavors – list and tuple.
- List: sequential array (fexible).
my_list = [1, 2, 3]
- Tuple: fixed sequential array (hashable).
my_tuple = (1, 2, 3)
Tuples are like Lists:¶
- They both hold an ordered sequence of object references. As such both are fast and efficient to copy and pass around.
- The values inside each can be retrieved with brackets. This is known as “indexing into” it.
first = some_group[0]
The first item is always index 0.last = some_group[-1]
The last item is always index -1. See Slicing for advanced indexing features. - The values in both can be visited or viewed in turn with a simple loop.
Sequence Slicing¶
A slice is like a view into a sequence, but it produces a new sequence of the same type. Both of the following examples work on any type of sequence. The author will leave it to the reader to determine which is more Pythonic. The first example is far more common, but don’t let that persuade you.
some_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
by_two = some_list[0:9:2]
print(by_two) # slice of 0-8 by strides of 2
some_tuple = tuple(some_list)
my_slice = slice(0, 10, 3)
by_three = some_tuple[my_slice]
print(by_three) # slice of 0-9 by strides of 3
Lists and Tuples are Heterogeneous¶
In some languages – tuples are used to hold heterogeneous data, as opposed to lists which are used to hold homogeneous data. In Python, both can do both and there is no technical reason to make this distinction. That said, generally speaking – for any type of sequence: homogeneous data is always preferred. One of the best containers for heterogeneous data is the dictionary, but there are other options aswell. Heterogeneous sequences are gross, don’t do it.
pythonic_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # homogeneous
ugly_list = ["this", 42, 3.14, 6+7j, pythonic_list] # heterogeneous
pythonic_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) # homogeneous
ugly_tuple = ("this", 42, 3.14, 6+7j, pythonic_tuple) # heterogeneous
print(ugly_list)
print(ugly_tuple)
print(pythonic_list)
print(pythonic_tuple)
my_list = [1, 2, 3, 4, 5]
for value in my_list:
print(value)
List Operations: Builtin Methods¶
my_list.append(0)
print(my_list)
my_list.reverse()
print(my_list)
my_list.sort()
print(my_list)
my_var = my_list.pop()
print(my_list)
my_other_var = my_list.pop(0)
print(my_list)
Python Tuple¶
Tuples have a cool name. It is pronounced “too-pul“, not “tup-pel“. The later would be spelled ‘tupple’.
Tuples are similar to lists, but with some key differences. The literal tuple is created with parens instead of brackets, but the values inside are indexed with brackets – just like lists. The other way to make a tuple is to cast it from a list.
In some cases, Tuples can be hashable. This ability comes at the cost of flexiblity. Tuples are fixed in size and order. You cannot append, pop, sort or shuffle a tuple directly. But you can always convert a tuple to a list and then back again. If you find yourself doing this a lot, please consider why it is that you’re not using a list in the first place. Lists are good.
The one time where only a tuple will do, is when you want to use a sequence as a key. For this to work – it needs to be hashable, and thereby contain no mutable values.
# Starting with a list
my_list = [1, 2, 3, 4, 5]
# Casting (Transforming) a list to a tuple
my_tuple = tuple(my_list)
# And back again
another_list = list(my_tuple)
print(my_list)
print(my_tuple)
print(another_list)
Complex Iteration¶
The zip()
function takes an arbitrary number of sequences and transposes them. Rows -> Columns. Zip then returns a container of tuples that is niether a list nor a tuple itself, but it is iterable.
names = ("John", "Paul", "George", "Ringo")
weights = ( 84.5, 81.8, 86.1, 92.2 )
heights = ( 1.84, 1.77, 1.90, 1.88 )
data = zip(names, weights, heights)
print("BMI Data:")
for n, w, h in data:
print(f"{n}:\t{w / h**2 : .3f}")
Tuple Performance is a Misnomer¶
In previous versions of Python – tuples were measurably faster than lists. Today, lists have nearly the same performance as tuples. What tiny performance difference there is should not be a concern to the modern Pythonista. If speed is really important: use a library (Numpy) with fast containers, design a Python Extension, or use something other than Python. Python is not about computational performance, it’s about developer performance.
Tuple Immutablity is a Misnomer¶
In a dark alley, late at night, you might hear something like the following…
“Your code will be safer if you use a tuple rather than a list. Tuples enforce immutability, so they are better. Always use a tuple!”
Unfortunately, this is bad advice. While the first sentence is true to some extent, the supporting argument is false and the whole idea is very misleading. Consider the following:
a, b, c = [], [], []
tup = (a, b, c)
print(f"tup => {tup}")
a.append(1)
b.append(2)
c.append(3)
print(f"tup => {tup}")
Tuples are not strictly immutable. It is far better to think of tuples as fixed in size and order. The values inside them are not necessarily immutable. Tuples are hashable, but only if they contain hashable values exclusively. Mutable objects are never hashable – even when stored inside a tuple.
The main point here should be the fact that tuples are potentially hashable and therefore can sometimes be used as keys in a dictionary. This may seem pointless now, but it’s actually one of the most powerful tools in Python. It is for this reason alone that we have the tuple container type in Python.
List or Tuple: Rule of Thumb¶
If the flexiblity of a list might be handy, use a list.
If your team lead says use a tuple unless you can’t – you should do that instead.