Using indexing notation with sequences (such as tuples) is very helpful. You can use indexing to retrieve individual items from sequences, or you can get a subsequence from a starting index to an ending index.
Notation
Retrieving individual items
The notation for individual item retrieval is tup[i]
, where i
is the value of the index. Remember, Python begins indexing at 0, up
until len(seq) - 1
.
Note: You MUST use
int
s as indices — using something likefloat
will raise aTypeError
.
>>> tup = (1, 2, 3, 4, 5)
>>> tup[0] # indices begin at 0
1
>>> tup[4] # indices end at len(tup) - 1
5
>>> tup[5] # improperly large indices raise IndexErrors
IndexError
>>> tup[2]
3
Positive indexing gives us the (i-1)th element from the beginning. What if we want to get the ith element from the end? Solution: use negative indices!
>>> tup # using the same tuple as before
(1, 2, 3, 4, 5)
>>> tup[-1] # get last element
5
>>> tup[-3] # get the third element from the end
3
>>> tup[-len(tup)] # improperly large index
IndexError
Subsequences
You can use slicing notation to retrieve subsequences from existing tuples. The resulting subsequence also a tuple.
>>> tup # using the same tuple as before
(1, 2, 3, 4, 5)
>>> tup[1:3] # starts at index 1 up to but not including index 3
(2, 3)
>>> tup[0:len(tup)] # one (not so good) way to get the whole tuple
(1, 2, 3, 4, 5)
>>> tup[-4:] # start: 4th item from end, to the very end
(2, 3, 4, 5)
Slicing notation has a nice shorthand notations:
>>> tup[:3] # ommiting the left-hand index starts from the very beginning
(1, 2, 3)
>>> tup[3:] # ommiting the right-hand index goes to the very end
(3, 4, 5)
>>> tup[:] # dupliates the entire tuple
(1, 2, 3, 4, 5)
When slicing, you can use indices that exceed the length of the tuple (although that's not good style). Python is smart enough to correct the mistake.
>>> tup[2:100] # 100 > len(tup)
(3, 4, 5)
>>> tup[3:2] # if left - index > right - index, return empty tuple
()
>>> tup[-100:] # abs(-100) > len(tup)
(1, 2, 3, 4, 5)
You can also specify the increment step-size for slicing. The
notation is tup[start:end:step]
.
>>> tup[1:4:2] # subsequence from index 1 up to index 4, but only getting every other item
(2, 4)
>>> tup[0:4:3] # subsequence from index 0 up to index 4, but only getting every third item
(1, 4)
>>> tup[:4:2] # subsequence from index 0 up to index 4, but only getting every other item
(1, 3)
>>> tup[1::2] # subsequence from index 1 up to end, but only getting every other item
(2, 4)
>>> tup[::2] # subsequence from beginning up to end, but only getting every other item
(1, 3, 5)
>>> tup[::-1] # get the entire tuple but in reverse.
(5, 4, 3, 2, 1)
Slicing other things
Slicing notation also works on strings
(and other built-in data
structures, like list
s, but you haven't learned about those yet).
>>> 'This works too!'
>>> s[5] # get the character at index 5
(5, 4, 3, 2, 1)
>>> s[5:10] # goes up to but not including index 5
'works'
>>> s[:-1] # everything up till the last character, '!'
'this works too'
Generator expressions
Python has a short hand for generating large iterables in a single line. The syntax for a generator expression is:
<expression> for <elem> in <iterable> if <boolean>
This will return a "generator object," which you can then convert to a tuple.
>>> tuple(i**2 for i in (1, 2, 3, 4, 5) if i % 2 == 0)
(4, 16)
>>> f = lambda x: x / 2
>>> tuple(f(elem) for elem in (1, 2, 3, 4)) # the if is optional
(0.5, 1.0, 1.5, 2.0)
map
, filter
, and other functions
In lecture, you learned about map
, filter
, and reduce
. Here are
some extra details about them.
map
In lecture, you saw that map
can take two arguments: a function;
and an iterable (e.g. a tuple). The function is applied to each item
in the iterable. map
returns a "map object," which can be converted
to tuples.
>>> tuple(map(lambda x: 2*x, (1, 2, 3, 4)))
(2, 4, 6, 8)
>>> tuple(map(lambda x: x**2, (1, 2, 3, 4)))
(1, 4, 9, 16)
There is an extended form of map
, which takes in three or more
arguments.
map(function, iterable1, iterable2, ...)
function
will be applied in parallel to all the iterables (all the
items, then all the second, then all the third). The function
must
take as many arguments as there are iterables. For example, if there
are three iterables, function
must take three arguments.
>>> tuple(map(lambda x, y: x + y, (1, 2, 3, 4), (4, 3, 2, 1)))
(5, 5, 5, 5) # (1 + 4, 2 + 3, 3 + 2, 4 + 1)
>>> tuple(map(lambda x, y, z: x + y + z, (1, 2), (3, 4), (5, 6)))
(9, 12)
Note: if the iterables are not of the same length, map will only go until the shortest iterable runs out.
filter
filter
just returns a new iterable, whose items are items in the
original iterable that passed the filter
function. The format of a
filter call is
filter(pred, iterable)
Here are some examples:
>>> tuple(filter(lambda x: x % 2 == 0, (1, 2, 3, 4, 5)))
(2, 4) # keeps even numbers
>>> tuple(filter(lambda s: len(s) > 3, ('hi', 'hello', 'fooply')))
('hello', 'fooply') # keep words whose lengths exceed 3
reduce
reduce
takes a iterable and uses a function to "combine" all the
items in the iterable. The result is usually an int. The format is
reduce(function, iterable)
function
must take in two arguments. Here are some examples.
>>> reduce(add, (1, 2, 3, 4, 5)) # sums numbers 1 through 5
15
>>> reduce(lambda a, b: a*b, (1, 2, 3)) # multiplies numbers 1 through 3
6
reduce
can also take an optional 3rd argument, a starting point.
>>> reduce(add, (1, 2, 3, 4, 5), 10) # sums numbers 1 through 5
25
enumerate
The enumerate
function can be applied in a for
loop to get the index of an item along with the item itself.
>>> tup = ('a', 'b', 'c')
>>> for i, item in enumerate(tup):
... print(i, item)
0 a
1 b
2 c
This is useful when you want both the index and the item in a loop (no need to initialize an index variable before the loop).
zip
The zip
function takes a number of iterables, and pairs together the
items in parallel. For example, it pairs together the first items,
then the second, then the third items. zip
returns a 'zip object',
which you can convert into a tuple.
>>> a = (1, 2, 3, 4)
>>> b = (5, 6, 7, 8):
>>> tuple(zip(a, b))
((1, 5), (2, 6), (3, 7), (4, 8))