Now that we know about lists, let's look at their immutable sibling, tuples.
The name tuple originates from the suffix of the sequence of n elements (single, couple/double/pair, triple, quadruple, quintuple, sextuple, septuple, ... n‑tuple)
Tuples (literals) are written just like lists, but without the square brackets:
>>> 1, 2, 3
(1, 2, 3)
>>> 1, # single element tuple with a trailing comma - not recommended
(1,)
The tuple literals are often written with round brackets. It is also the way Python prints them:
>>> (1, 2, 3)
(1, 2, 3)
>>> (1,) # single element with a trailing comma with brackets - recommended
(1,)
>>> () # empty tuple - cannot be written as a literal without brackets
()
Alternatively, you can also create tuples with the tuples
constructor:
>>> tuple() # empty tuple
()
>>> tuple(range(3)) # accepts anything which can be used in for loops
(0, 1, 2)
With exception of the empty tuple, tuples are made by the commas rather than by the round brackets. However, we very often need to surround them with round brackets to achieve our goal. The tuples therefore may sometimes look like "lists written with round brackets" even though there are not.
Fell free to use tuples without the brackets but, be aware that there are cases where they can lead to ambiguities and errors:
>>> 1, 2, 3 + 4, 5, 6
(1, 2, 7, 5, 6)
>>> (1, 2, 3) + (4, 5, 6)
>>> (1, 2, 3, 4, 5, 6)
... or:
>>> 1, 2, 3 == 4, 5, 6
(1, 2, False, 5, 6)
>>> (1, 2, 3) == (4, 5, 6)
False
Always use round brackets when passing tuples as function arguments to distinguish them from function arguments:
>>> sorted(2, 4, 5, 1, 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sorted expected 1 argument, got 5
>>> sorted((2, 4, 5, 1, 0))
[0, 1, 2, 4, 5]
It is highly recommended to write single element tuples with brackets '(1,)'.
The trailing comma looks like a typo 1,
and the extra round brackets improve
clarity of the code.
Tuples behave almost like lists, but they cannot be modified.
I.e., they don't have methods like append
and pop
,
elements cannot be assigned or removed:
>>> my_tuple = (1, 2, 3)
>>> my_tuple.append(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'
>>> my_tuple.pop(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'pop'
>>> my_tuple[1] = -2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> del my_tuple[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
A "modification" of a tuple always requires creation of a new tuple object.
Tuple manipulations using Python asterisk unpacking:
>>> sample = (1, 2, 3)
>>> *sample, 4
(1, 2, 3, 4)
>>> *sample, *sample[-2:]
(1, 2, 3, 2, 3)
>>> *sample[:1], -2, *sample[2:]
(1, -2, 3)
>>> *sample[:1], *sample[2:]
(1, 3)
*
in the expressions unpacks the given sequences (list, tuple, range, ... etc.).
Mixing list and tuples in these expressions is possible.
FYI, this also works with lists. Try [*sample[:1], -2, *sample[2:]]
.
... or +
concatenation of tuples:
>>> sample = (1, 2, 3)
>>> sample + (4,)
(1, 2, 3, 4)
>>> sample + sample[-2:]
(1, 2, 3, 2, 3)
>>> sample[:1] + (-2,) + sample[2:]
(1, -2, 3)
>>> sample[:1] + sample[2:]
(1, 3)
Tuple concatenation with the +
operator works only with tuples.
Mixing list and tuples in these expressions leads to an error.
Tuples can be to converted from/to lists:
>>> tuple([1, 2, 3]) # list to tuple
(1, 2, 3)
>>> list((1, 2, 3)) # tuple to list
[1, 2, 3]
Tuples can be nested. Mixing nested list and tuples is possible:
>>> 42, ("Hotel", "Golf", "Tango", "Golf")
(42, ('Hotel', 'Golf', 'Tango', 'Golf'))
>>> "John", "Doe", [3, 4 , 2], True
('John', 'Doe', [3, 4, 2], True)
>>> [(1, 1), (0, 4), (4, 5)]
[(1, 1), (0, 4), (4, 5)]
Beware that nested mutable value (list) makes a tuple object mutable with all the possibly undesired consequences.
Tuples can be used in for
loops
and they can read individual elements.
people = 'mom', 'aunt', 'grandmother'
for person in people:
print(person)
print('First is {}'.format(people[0]))
Does this look familiar?
We have already used tuples in
for greeting in 'Ahoj', 'Hello', 'Hola', 'Hei', 'SYN'
Short tuples can also be used to simplify conditions:
if code in (1, 4, 7):
# do something ...
is equivalent to
if code == 1 or code == 4 or code == 7:
# do something ...
Tuples are used in functions with variable number of arguments:
>>> def print_sum(*values): # values variable holds a tuple of free arguments
... print(f"sum({values}) = {sum(values)}")
...
>>> print_sum(1, 2)
sum((1, 2)) = 3
>>> print_sum(1, 2, 4, 5)
sum((1, 2, 4, 5)) = 12
Tuples are used if we need to return more than one value from a function. You simply declare the return values separated with comma. It looks like you're returning multiple values, but in fact, it is just one tuple being returned:
def floor_and_remainder(a, b):
return a//b, a%b
Such a floor_and_remainder
function already exists
in Python: it's called divmod
and it's always
available (you don't have to import it).
Python can do another trick: if you want to assign values into several variables at once, you can just separate the variables (the left side) by a comma, and the right side can be some "compound" value - for example a tuple:
floor_number, remainder = floor_and_remainder(12, 5)
A tuple is the best for this purpose, but
it works with all the values that can be used with a for
loop:
x, o = 'xo' # is like: x = 'a'; o = 'o'
one, two, three = [1, 2, 3] # is like: one = 1; two = 2; three = 3
a, b = 1, 2 # is like: a = 1; b = 2
first, *body, last = range(4) # is like: first = 0 ; body = [1, 2], last = 3
*
in the last assignment grabs anything between the first
and last elements and stores it as an array.
zip
is an interesting function.
It is used in for
loops, just like the range
function that returns numbers.
E.g., when zip
gets two lists (or other values that can be used in a for
loop),
it returns pairs -- the first element of the first list is paired with
the first element of the second list,
then the second element with the second, the third element with the third and so on.
It is useful when you have two lists with the same structure - the relevant elements "belong" together and you want to process them together:
>>> list(zip([3, 1, 2], ["a", "b", "c"])
[(3, 'a'), (1, 'b'), (2, 'c')]
people = 'mom', 'aunt', 'grandmother', 'assassin'
properties = 'good', 'nice', 'kind', 'insidious'
for person, property in zip(people, properties):
print ('{} is {}'.format(person, property))
Note the tuple multiple assignment in the for statement.
When zip
gets three lists it will return triplets, and so on.
The other function that returns pairs is enumerate
.
As an argument, it takes a list (or other values that can be used in a for
loop)
and it pairs up the element's index (its order in the list) with the respective element.
So the first element will be (0, first element of the given list), then
(1, second element), (2, third element) and so on.
>>> list(enumerate(["a", "b", "c"])) # count from 0
[(0, 'a'), (1, 'b'), (2, 'c')]
>>> list(enumerate(["a", "b", "c"], 1)) # count from 1
[(1, 'a'), (2, 'b'), (3, 'c')]
prime_numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
for i, prime_number in enumerate(prime_numbers):
print('Prime number on position {} is {}'.format(i, prime_number))
Lists are used when you do not know in advance how many values you will have, or when there are a lot of values. Simply use lists for list of things, e.g., a list of words in a sentence, a list of contest participants, a list of moves in a game, or a list of cards in a deck.
Tuples are often used for values
of different types where each "position"
inside the tuple has a different meaning.
For example, you can use a list for the letters of the alphabet,
but for pairs of index-value from enumerate
, you'd use a tuple.
The empty and one-element tuples are little strange, but they exist, and Python would not be complete without them.
Both lists and tuples also have limitations or benefits (depending on your point of view). Tuples cannot be changed, and when we will learn how to work with dictionaries, we will find that lists cannot be used as dictionary keys.
Often, it is not entirely obvious which type to use -- in that case, it probably doesn't really matter. Follow your instinct. :)
Let's get back to our table_parser
.
SOURCE_TRANSLITERATION_TABLE_UA_GB = """
а a
б b
в v
г h
ґ g
д d
е e
є ye
ж zh
з z
и ȳ
і i
ї yi
й ĭ
к k
л l
м m
н n
о o
п p
р r
с s
т t
у u
ф f
х kh
ц ts
ч ch
ш sh
щ shch
ь ʼ
ю yu
я ya
’ ˮ
"""
def parse_table(source):
""" Parse string table. """
table = []
for line in source.splitlines():
row = line.split()
if row: # ignore empty lines
table.append(row)
return table
table = parse_table(SOURCE_TRANSLITERATION_TABLE_UA_GB)
print(table)
parse_table
function so that it produces table as a list of
tuples rather than a list of list.add_capitals
function which extend the table by adding capital
letters. Use to string.upper
for the source Cyrillic letters and
str.capitalize
for the Latin transcription.