Search⌘ K
AI Features

Tuples, Lists, and Strings Tips

Explore important Python tips for working with tuples, lists, and strings within data structures. Understand how to correctly create one-element tuples, improve string readability with raw strings, unpack sequences efficiently, and flatten nested lists. Learn the advantages of immutable tuples over lists to write safer and faster Python code, and when to prefer dictionaries for optimized search operations. This lesson arms you with practical knowledge to select and use Python data types effectively for robust programming.

Python includes a strong collection of data types and data structures. We’ll revisit this phrase and its variations throughout this chapter. Choosing the appropriate representation for our data may be vital. In the best case, an incorrectly chosen data type may cause significant performance degradation. In the worst case, it may cause logical errors and lead to incorrect results.

This chapter provides tips about both traditional and“obscure data structures and types, including standard containers, counters, and various numbers. We’ll explore how to:

  • Work with complex numbers, rational numbers, and infinities
  • Easily create modules
  • Transform lists
  • Count items
  • Recognize the immutability of tuples

The chapter also includes suggestions about advanced class design (class attributes and customized object print-outs).

Construct a one-element tuple

Creating a one-element tuple is difficult. Let’s first try to make a one-element tuple similar to one-element lists and one-element sets:

Python 3.8
print(type([0]))
print(type({0}))
print(type((0)))

The result isn’t a tuple, but an integer number; the first and only element itself. That’s because parentheses in Python have several uses. They participate in creating a tuple (in cooperation with commas), define functions, define subclasses, invoke functions, and change the order of evaluation, to name a few. In the last example, the outer pair of parentheses invokes the function and the inner pair changes the order of evaluation!

To inform Python that a tuple is born, add a comma after the first element. It’s the comma that builds a tuple, not the parentheses.

Python 3.8
print(type((0,)))
a=0, # Just a comma!
print(type(a))

Perhaps even the inner parentheses are redundant. Can we eliminate them? Let’s calculate the length of a one-element tuple.

Python 3.8
print(len(0,))

The error is that the comma also has several uses. It creates tuples, but it also separates arguments in a function call and parameters in a function definition. In the example above, Python considered the second use and treated 0 as the first argument. We could potentially avoid one-element tuples. We can replace a one-element container that isn’t expandable with a single scalar variable.

Improve readability with raw strings

Raw strings are prefixed with the letters, r or R, outside of the quotation marks. Within a raw string, the escape character—backslash ‘’—doesn’t have a special meaning. It’s no longer an escape character, merely a backslash. All special compound characters, such as ‘\n’ and ‘\v’, therefore lose their special meanings and become two-character strings:

Python 3.8
print(r'\n' + '\n' + r'\n', len('\v'), len(r'\v'))

Consider a string that has many backslashes, like a regular expression. In a cooked (not raw) string, each backslash must be prefixed by another backslash, creating many indecipherable backslashes, much like this paragraph:

Python 3.8
regex = '\\n\\\.\\\\n'
print(regex)

Note: The regular expression above matches a string that consists of a line break, followed by a literal backslash, a period, another literal backslash, and another line break. Raw strings improve readability:

Python 3.8
regex = r'\n\\.\\n'
print(regex)

What if we want to have a special escaped character in a raw string? That isn’t directly possible. Either revert to the cooked strings or combine a raw and cooked string with string concatenation:

Python 3.8
mixed_string = '\n' + r'\\.' + '\n'
print(mixed_string)

Finally, a backslash at the end of a raw string still acts as an escape character. The r'\' string isn’t a single backslash, it’s an unterminated string.

Python 3.8
print(r'\')

Unpack lists and tuples

You can extract individual items from a sequence (such as tuple, list, or string) using the indexing operator:

Python 3.8
seq = 1, 2, 3, 4
x = seq[0]
y = seq[1]
z1 = seq[2]
z2 = seq[3]

Another way is to resort to multiple assignments (also known as simultaneous assignments). The number of items on the left must match the sequence size.

Python 3.8
x, y, z1, z2 = seq
print(x,", ",y,", ",z1,", ",z2)

Multiple assignments work best if the number of items in the sequence is known and doesn’t change because we have to list the variables on the left-hand side of the assignment and those variables must match the sequence element-wise.

There’s a problem, though. We can use the star (*) operator to collect the remaining items from the sequence, even if we aren’t sure about the sequence size. For now, know that the sequence has more than one item. The following statement unpacks a sequence into the variables, x (the first element), y (the second element), and z (the remaining elements as a list). The list is empty if the sequence has only two items:

Python 3.8
x, y, *z = seq
print(x, y, z, sep=' | ')

The starred variable on the left-hand side needn’t be the last one. It can be anywhere in the middle and even at the beginning of the statement. That said, we can’t use more than one star. Otherwise, matching isn’t possible:

Python 3.8
*x, y, z = seq
print(x, y, z, sep=' | ')
*x, y, z = seq[:2] # Take the first two elements
print(x, y, z, sep=' | ')
#And a little string example:
start, *rest, end = 'Hello, world'
print(start, ''.join(rest), end, sep=' | ')

Print a list

If we tried to print a Python list in a human-readable way—without the square brackets, commas, and quotation marks— print(l) isn’t an ideal solution:

%0 node_1 1 node_2 2 node_3 3 node_1636352112529 4 node_1636352146967 5
Python 3.8
l = list('hello') + list(range(5))
print(l)

What we need is a way to convert each list item to a string with str() and to combine the strings with a delimiter of our choice (say, whitespace) and the str.join() method. A list comprehension is ideal:

%0 node_1 h node_2 e node_3 l node_1636352568703 l node_1636352621341 o node_1636352565710 0 node_1636352571257 1 node_1636352631732 2 node_1636352623882 3 node_1636352574226 4

Let’s add a space after each character in the list.

Python 3.8
print(' '.join(str(x) for x in l))

The missing square brackets around what looks like a list comprehension isn’t a mistake. Instead of list comprehension, we used a comprehension expression to further clarify this under-used mechanism.

Python has many other comprehension expressions. Set comprehension is enclosed in curly braces {} and acts almost like list comprehension, except that the result is a set with the duplicates removed. We could accomplish the same effect by applying set() to list comprehension, but set comprehension is considerably faster.

Let’s try it with brackets.

Python 3.8
print(' '.join([str(x) for x in l]))

If the list is recursive—containing other compound items such as lists, tuples, and sets, or any combinations of them—we can combine printing with flattening.

Flatten that list

Flattening a list converts it to a list where each item is of a simple type: a number, a string, a boolean value, or a None. If a list contains only simple items, it’s already flat. Some functions and other expressions produce nested lists, such as a list of lists. The nestedness of a list may be higher than two (think of a list of lists of lists)

In the code below, we flatten a list of characters.

Python 3.8
def flatten(seq):
return ' '.join((flatten if isinstance(x, (list, tuple, set)) \
else str)(x) for x in seq)
letters=list('hello')
print(flatten([[letters], [letters, 1, 2, (3,)]]))

Some important remarks

  1. letters is a better name for the throw-away list than a list.

PBuilt-in functions in Python aren’t protected against vandalism. The most common way to vandalize them is to redefine their identifiers. That happens when we want to give a simple, clear name to a new variable. For example, why not call a new list? This statement, list = [2, 4, 6, 8, 10], is fine as long as we or anyone else reusing our code doesn’t attempt to create another list by using the class constructor. list isn’t a constructor anymore; it’s not even a function. It’s a list, just as we wanted it before we manipulated the rest of our code.

  1. The list() function , or the list constructor, converts everything to a list, even a string.
  2. The isinstance() function is a better type checker than the type() function.
  1. The conditional operator decides which function to apply to each item on the list. It chooses one of the function names, flatten or str, and then calls the chosen function by name.
  2. The list comprehension isn’t enclosed in the square brackets, which is correct…

The flatten() function is even more versatile than we may have thought.

Carve it in stone

It seems nothing can be achieved with tuples that can’t also with lists. For any practical purpose, lists look like mutable tuples. A tuple has the same attributes and methods as a list (except for the obscure method, tuple.__getnewargs__()), as confirmed by retrieving a set of attributes and methods for each class and taking the set difference.

The code below shows the set difference between a list and a tuple.

Python 3.8
print(set(dir(list))-set(dir(tuple)))

Why would anyone use tuples, then? There are two reasons for this, both related to their immutable quality.

What is a tuple?

A tuple is one of the few immutable data types in Python, behaving like a constant. Once we create a tuple, we cannot add, remove, or modify its items. If we want a collection of items that aren’t expected and shouldn’t change during the program execution, we’d use a tuple. Let’s say we want to create a collection of frequently used constants and then incidentally change the value of ππ:

Python 3.8
r=1 # You can initialize value of r
some_constants = [math.pi, math.e, 1, 0]
PI, E, ONE, ZERO = range(4)
some_constants[PI] = 4 # Oops...
area = some_constants[PI] * r * r
print(area)

This trick won’t work with a tuple, and as a result, the following code results in an error.

Python 3.8
some_constants = (math.pi, math.e, 1, 0)
some_constants[PI] = 4

Remember that it’s a comma that creates a tuple, not parentheses:

some_constants = math.pi, math.e, 1, 0

Tuples protect their content from intentional and unintentional corruption. Additionally, they don’t have a hidden mechanism for contraction and expansion. The absence of said mechanism makes them faster to create than lists of the same size. The difference with small tuples (smaller than 128 items) is dramatic, by a factor of 2–20. The larger tuples are still more efficient, but not as much as the small tuples. Use tuples instead of lists whenever possible.

TL:DR: Make your code safer and faster!

No Trees? Use a dict()

Python includes a strong collection of data types and data structures.

More data types and data structures come with the standard library. There seems to be a suitable container for any occasion. For more details, see Waste Space, Save Time).

The collection doesn’t include trees, binary or otherwise. Why not?

A common use for trees is searching. The worst-case time complexity for searching a balanced binary search tree is O(logN)O(\log N), a definite improvement over linear lists that we can search in the O(N)O(N) time.

Python dictionaries, a built-in data type, offer an average search time complexity of O(1)O(1). This is unsurpassable, so we should use a dict.

Quiz on tuples, lists, and strings

Technical Quiz
1.

What is the output of this given code?

tuple=(1,2,3,4)#Declaring a tuple
tuple[2]=5# changing the 3rd index value of tuple
print(tuple)#printing the tuple values
A.

Error

B.

(1, 2, 5, 4)

C.

(1, 2 , 3 ,5)


1 / 5