How to Browse Sequences
We already had a taste of what sequences are thanks to strings. But strings only include text. What if we need to store a bunch of numerical values? Or some strings together with numerical values?
Python provides two standard data types for containers where order matters: a mutable one, list()
, and an immutable one, tuple()
. They store an ordered sequence of value references. This means that if a list contains another list – of course this is possible – the inner one will not be copied inside the container, but a reference to it will be stored.
In Python a list is delimited by the characters []
, while a tuple is delimited by characters ()
.
Here’s a list:
Now, this is a tuple:
As we said, elements in lists and tuples are of arbitrary nature: they can be any kind of object, None
included. They can be empty too.
Emptiness usually does not make much sense for tuples, given they cannot be modified once created. Instead it is very common to initiate an empty list which will be populated during an iterative process triggered by a while
or for
construct. Consider the following example:
The constructors list()
and tuple()
accept any kind of iterable as argument like strings or lists or tuples. They “listify” or “tuplefy” their arguments. For example, if a string is passed as argument to a list()
function, the result will be a container where each character is stored separately:
At this point, you might ask yourself: why both tuples and lists? Aren’t lists enough? Apart from the difference in terms of mutability, there is a semantic difference in the usage of tuples and lists.
While tuples are used to store heterogeneous data structures, lists are ordered sequences of the same stuff. Take note that they have the same degree of freedom concerning data types, it’s just a matter of semantics.
Let’s consider the following scenario: it’s time for a quick ride on our bike and we would like to track our journey using an application installed on a smartphone. Let’s assume this application is written in Python. Every few seconds the application will ask the smartphone hardware for:
- gps coordinate x
- gps coordinate y
- date and time
This data will be then organized in a tuple. Since the length of the container does not need to be flexible (e.g. we know we won’t add a z index to it) but just to record a state through multiple values, a tuple is a good choice.
The application’s goal is to track a route, which is a sequence of multiple position instances. So, the application needs a container which is flexible and can be extended until the end of the journey: a list.
Once finished, the route of the journey is saved on the smartphone memory. A standard table would suit very well the purpose.
Sequence Properties for Lists and Tuples
- accessing
- slicing
- check containment
- test equality
- natural order
- concatenation
List Methods
As we said, a list is a flexible container, which means that once created, it can be manipulated in multiple ways. Python provides a few specific methods for it
.append(item)
.extend(iterable)
.insert(index, item)
exercise 12.1
transform a right-angled triangle in a square adding a point
.remove(item)
.pop(index)
.index(item)
.sort()
.reverse()
For Syntax
Python provides a for-loop syntax which is useful to iterate over a series of elements. Where while
comes in handy when we need to keep doing something until a condition is matched, for
is preferable when we need to browse a container. for
can be used with any kind of iterable, which is a wider notion than sequences. Technically, an iterable can be non-sorted (as sets or dicts).
Here’s the general for
construct:
Note that the body is four spaces indented rightwards. for
and in
are protected keywords. eachElement
is an identifier to which the body can refer to. Of course its name is arbitrary, it only needs to respect the standard rules for identifiers.
The iterable can be created either before the for
opening statement or created on the spot. Meaning that this example:
is equivalent to:
This also means that we could invoke a function which is able to generate an iterable on the spot
Python provides a function that you will use very often in your coding routine: range()
. This function returns an iterator –not a real list– which will provide a sequence of integers according to the following arguments
start
and step
are optional. start
has to be addressed if step
is defined. Take into account that, as for the slicing notation, the stop
value is not included into the range created.
This function will make the iteration over a sequence of integer number easy and compact:
After the end of the loop the identifier will be still available in the program namespace, assigned to the last element of the iterable.
How can this be useful in a graphic design application? Consider the following scenario: you would like to create a pdf document of sixteen pages and write on each page a sequential page number. DrawBot can do that of course:
A for construct is the perfect companion of an .append()
list method. It is very common to create a series of values starting from a pre-existing one. Like making an all-caps version of a word sequence:
Drawing Many Times in One Direction
Just like the while
construct, for
can be very helpful to draw shapes repetitively. In this way your code will look compact and it will be easy to edit.
Instead of:
go for this kind of structure
The advantages of the second version are enormous, starting for the very basic fact that you can add and subtract elements with almost no changes.
What if you need to add points at the beginning and at the end? Easily done.
Along with drawing, we can perform any kind of calculation. For example, the fill color of a shape could be defined by the identifier provided by the for construct. Here’s an example.
Or we can affect the arguments used to draw the shape itself:
Remember that if
statements can be used in the for
body, meaning that we can perform choices within an iterative process:
Drawing Many Times in Two Directions
A single for
construct allows repetition in one dimension. But, as we have seen with the previous example, other constructs can be used inside the for
body. Which means that a second for
loop can be nested into the first for
loop. In this way the repetition becomes two dimensional.
If we need to draw a table or 90° based pattern, this technique becomes very handy. Consider the following drawing
This is a bidimensional matrix, it is a simple table. Each element of the matrix is called cell. Each cell of this matrix can be described by two indexes: the index of the column and the row to which the cell belong. If we explicit write each index to each cell we obtain
At this point we only need to define a size for the cell and we are able to draw the left column of the matrix
or the bottom row the matrix
If we combine the two constructs together we can draw the entire matrix calling the rect()
function only once
Matrix indexes can be drawn very easily
As for the single for
construct, calculations performed within the body can affect the quality of the drawing. A one-dimensional gradient can easily become bi-dimensional
and an if
statement nested into the inner for
loop can draw a chess board:
Please, note that nested for constructs can also be used to navigate nested data structures as a list of tuples: