Python Part I · Module 6 · Tuples
Tuple Methods and Converting Between Tuple and List
This chapter is short, and the reason it is short is itself the lesson. A tuple has exactly two methods. That is not a gap in the language — every list method that is missing is one that would change the collection, and Chapter 6.4 explained why a tuple has none of those. What this chapter adds is the two that remain, and the conversion route for the case where you genuinely do need to change something: turn the tuple into a list, change the list, turn it back. That route is easy to write and easy to over-use, and knowing when not to reach for it is what separates a deliberate choice from a habit.
Foundation11 min readPythonPart IFundamentalsTuplesConversion
Part I · Module 6 · Chapter 6.6 · Tuple Methods and Converting Between Tuple and List
1. count(): How Many of This Element
t = ("a", "b", "a")
print(t.count("a"))
print(t.count("c"))2
0count returns how many elements equal the value you give it, and zero for one that is not there — never an error. Chapter 4.10 made exactly that point about str.count, and it holds here.
What differs from the string version is what counts as a match. On a string, count looked for a substring. On a tuple, it compares whole elements:
st = ("PASS", "FAIL", "PASS", "PASS")
print(len(st), st.count("PASS"), st.count("FAIL"))4 3 1Four elements, three of which are "PASS". st.count("PA") would be 0, because no element is "PA" — the same whole-element rule Chapter 5.1 established for in.
2. index(): Where Is It
t = ("a", "b", "a")
print(t.index("b"))
print(t.index("a"))1
0index returns the position of the first element equal to the value. "a" appears twice and the answer is 0, the earlier one.
That position is an ordinary index, immediately usable as one:
sigs = ("clk", "reset_n", "valid")
pos = sigs.index("reset_n")
print(pos)
print(sigs[pos])1
reset_nAnd when the value is absent, index raises rather than returning a sentinel:
t = ("a", "b")
print(t.index("z"))ValueError: tuple.index(x): x not in tupleChapter 4.10 drew this distinction for strings, where find returned -1 and index raised. A tuple has no find — only the raising version. So if you are not certain the value is present, test with in from Chapter 6.1 first.
3. That Is the Entire Interface
Two methods. Not a selection of the useful ones — all of them.
Look at what is missing and the pattern is immediate: append, insert, remove, pop, clear, extend, sort, reverse. Every one of those changes a collection, and Chapter 6.4 established that a tuple has no way of being changed. So there is nothing for them to do, and they do not exist:
t = ("a",)
t.append("b")AttributeError: 'tuple' object has no attribute 'append'Everything else you might want is not a method at all, and you already have it: len(), in, indexing, slicing and + all work on tuples and none of them is owned by the type.
4. Converting: list() and tuple()
Chapter 5.9 introduced list() for building a list out of anything that supplies values in order. A tuple is one of those:
items = list(("a", "b"))
print(items, type(items))['a', 'b'] <class 'list'>tuple() is the mirror image and works the same way:
print(tuple([1, 2]))
print(tuple("abc"))(1, 2)
('a', 'b', 'c')A list becomes a tuple with the same elements; a string supplies its characters, exactly as it did for list() in Chapter 5.9.
The edges are unsurprising:
print(tuple(), type(tuple()))
print(tuple() == ())() <class 'tuple'>
Truetuple() with nothing is the empty tuple. Write () — it is shorter, and tuple() earns its place when it is converting something.
And a value with no contents is refused, with the message Chapter 5.9 explained:
print(tuple(5))TypeError: 'int' object is not iterable5. The Round Trip
Put the two together and you have the route for changing something that is currently a tuple:
t2 = ("a", "b")
items = list(t2)
items.append("c")
t3 = tuple(items)
print(t3)
print(t2)('a', 'b', 'c')
('a', 'b')Three steps: convert to a list, use any list operation you like, convert back. And t2 is unchanged — it could not have been.
The intermediate list is genuinely independent, which Chapter 5.8's rules guarantee:
tt = (1, 2)
ll = list(tt)
ll.append(3)
print(tt, ll)(1, 2) [1, 2, 3]list() built a new list; changing it has no route back to the tuple. There is no aliasing hazard here at all — the conversion is a clean break.
6. Deliberately, Not by Accident
Now the part the canonical curriculum singles out for this chapter, and it is a judgement rather than a technique.
The round trip is three easy lines, which makes it tempting. Used in the right place it is exactly right: you have a fixed record, something genuinely exceptional needs to produce a modified version of it, and you build that version explicitly.
Used in the wrong place it is a sign the data was mis-typed from the start.
Two cases, to make the line concrete.
Reasonable. A configuration record arrives as a tuple, and one field needs correcting once before it is passed on. Converting, changing, converting back is explicit and local, and the result is still a fixed record afterwards.
Not reasonable. A collection of results that gains an entry every time something finishes, stored as a tuple and round-tripped on each addition. That collection grows — it is a list, and Chapter 5.3 is about it. Writing it as a tuple and converting repeatedly is slower, longer and misleading to the reader.
The underlying principle is one you have met twice. Chapter 4.3 explained why repeated concatenation is the wrong tool for assembling many pieces, and Chapter 5.6 explained why + and extend are different choices. Each time the question was the same: is this collection in motion or not? Chapter 6.7 is that question in full.
7. Predict, Run, Observe
Predict every line before reading on. One raises.
t = ("PASS", "FAIL", "PASS")
print(t.count("PASS"))
print(t.count("SKIP"))
print(t.index("FAIL"))
print(len(t))
print(tuple(list(t)) == t)
print(t.index("SKIP"))The verified output for the first five:
2
0
1
3
Trueand the sixth:
ValueError: tuple.index(x): x not in tuplecount("SKIP") is 0 — safe. index("SKIP") raises — not safe. That asymmetry is Section 2 and it is the thing to remember about these two.
tuple(list(t)) == t is True: the round trip with no change in the middle gives a tuple equal to the original. It is not the same tuple, but for an immutable value that distinction has no observable consequence — which is itself a small illustration of why immutability simplifies things.
8. Exercises
Exercise 1 — ask the two questions. For st = ("PASS", "FAIL", "PASS", "SKIP"), write expressions giving: how many passed, the position of the first failure, whether anything was skipped, and the total number of results. Use the right tool for each.
Exercise 2 — predict. Without running anything, give each result, then verify. One raises; name the error:
(a) st.count("PASS") (b) st.count("pass") (c) st.index("SKIP") (d) st.index("ERROR") (e) len(st)
Exercise 3 — safe or not. Explain in two sentences why count can always be called safely while index cannot, and give the one-line test you would run before calling index.
Exercise 4 — why only two. List four list methods a tuple does not have, say what they have in common, and explain in one sentence why their absence follows from Chapter 6.4.
Exercise 5 — the round trip. Starting from cfg = ("clk", 100), write the three lines that produce a new tuple ("clk", 200) using a list in the middle. Then write the one-line alternative using slicing and +, and say which you would prefer.
Exercise 6 — convert both ways. Predict the value and type of each, then verify:
(a) list(("a", "b")) (b) tuple(["a", "b"]) (c) tuple("ab") (d) tuple() (e) tuple(("a",))
Exercise 7 — the judgement. A colleague stores the failures found during a run as a tuple, and each time a failure appears they convert to a list, append, and convert back. Write three sentences saying what is wrong with that, what it suggests about the data, and what you would change.
Exercise 8 — independence. Predict both prints and explain in one sentence why the tuple is unaffected:
t = (1, 2)
l = list(t)
l.append(3)
print(t)
print(l)9. Summary
- A tuple has exactly two methods:
countandindex. That is the complete interface. - Every missing list method is one that would change the collection — which Chapter 6.4 established a tuple cannot do. The short interface is a design statement, not a gap.
count(x)returns how many elements equalx, and0if none. It is always safe to call.- It matches whole elements, not substrings — unlike
str.count. index(x)returns the position of the first matching element, and raisesValueError: tuple.index(x): x not in tuplewhen absent. A tuple has nofind.- Test with
inbefore callingindexif presence is not guaranteed. len(),in, indexing, slicing and+all work and are not methods of the type.list(t)andtuple(l)convert in both directions;tuple("ab")supplies the characters, andtuple(5)isTypeError: 'int' object is not iterable.- The round trip — list, change, tuple — is the route for modifying a record, and the intermediate list is fully independent.
- Use it deliberately. Routinely converting a tuple to change it means the data was a list all along.
One question remains, and it is the one this whole module has been building towards.
Where this fits
Part of the Python curriculum.
