Python Part I · Module 6 · Tuples
The One-Item Tuple Trap
This is the shortest idea in the module and it has its own chapter for a reason: it catches essentially everyone exactly once, and when it does, nothing raises. A tuple with one element needs a trailing comma, and a pair of parentheses with one thing inside them is not a tuple at all — it is just that thing. Stated as a rule that sounds like trivia. The reason it matters is that one real Python operation you have already used takes a group of options, and writing that group with one option and no comma does not produce an error. It produces a different, perfectly plausible, wrong answer.
Foundation10 min readPythonPart IFundamentalsTuplesSyntax
Part I · Module 6 · Chapter 6.2 · The One-Item Tuple Trap
1. The Trap
Here it is, in four lines:
not_a_tuple = (5)
print(not_a_tuple)
print(type(not_a_tuple))5
<class 'int'>Parentheses, one value inside, and the result is an int. Not a tuple of one element — the number five.
Now with one character added:
real_tuple = (5,)
print(real_tuple)
print(type(real_tuple))(5,)
<class 'tuple'>A trailing comma, and it is a tuple. Python even prints it back with the comma still there, which is its way of telling you this is a one-element tuple and not a number in brackets.
2. Why This Is the Only Possible Answer
If you learned Chapter 6.1's rule, this is not something to memorise — it follows.
The comma makes the tuple. The parentheses group.
So in (5) there is no comma, which means there is no tuple. The parentheses are doing what parentheses have done since Chapter 3.8: grouping an expression. (5) is five for exactly the reason (2 + 3) is five — brackets round a single value change nothing about it.
And in (5,) there is a comma, so there is a tuple, with one element.
The parentheses are not even required:
also = 5,
print(also, type(also))(5,) <class 'tuple'>A bare 5, is a one-element tuple. That looks like a typo and is valid Python — which is itself a decent argument for keeping the parentheses as a visual signal, even though they are not what creates the tuple.
3. The Same Trap With Any Type
Nothing about this is specific to numbers:
s = ("PASS")
t = ("PASS",)
print(type(s), len(s))
print(type(t), len(t))<class 'str'> 4
<class 'tuple'> 1("PASS") is the string "PASS" — four characters. ("PASS",) is a tuple holding one string — one element.
Look at what len() did there, because it is the fastest diagnostic you have:
4. Two or More Need Nothing Special
The trailing comma is only load-bearing for one element. From two upwards the commas between the elements already do the work:
two = (5, 6)
print(two, type(two), len(two))(5, 6) <class 'tuple'> 2You may still put a trailing comma on a longer tuple, and it is harmless:
three = (1, 2, 3,)
print(three, len(three))(1, 2, 3) 3Three elements, not four — a trailing comma never adds an empty element. This is the same convention Chapter 5.7 recommended for multi-line list literals, where a trailing comma makes adding a line a one-line change.
5. Where It Actually Bites
Everything so far is a type() curiosity. Here is the case that makes this chapter worth its own page, and it is a real bug shape.
Chapter 4.10 showed endswith accepting several alternatives grouped together — and Chapter 6.1 noted that the group is a tuple. Checking a file against two extensions works like this:
print("top.sv".endswith((".sv", ".v")))
print("top.v".endswith((".sv", ".v")))True
TrueBoth match, which is what you wanted. Now suppose the requirement narrows to one extension and somebody edits the line down, dropping the now-lonely comma:
print("top.sv".endswith((".v")))
print("top.v".endswith((".v")))False
TrueNo error. The code runs, returns booleans, and quietly stops matching .sv files — because (".v") is not a one-option group, it is the plain string ".v", and "top.sv" does not end with ".v".
Written correctly, with the comma:
print("top.sv".endswith((".v",)))
print("top.v".endswith((".v",)))False
TrueThe same two results — because in this instance one option really was intended. The point is not that the corrected version differs here; it is that you cannot tell from the output which version you wrote. One of them means "any of these options" and the other means "this exact string", and they coincide often enough that the mistake survives testing.
6. Two Failures That Do Raise
Not every version of this mistake is silent. When the non-tuple is then used as a tuple, Python objects.
Concatenating:
print((1, 2) + (3))TypeError: can only concatenate tuple (not "int") to tupleThat message is the same shape Chapter 4.3 and Chapter 5.6 showed for strings and lists. The author meant (3,):
print((1, 2) + (3,))(1, 2, 3)Indexing:
x = (5)
print(x[0])TypeError: 'int' object is not subscriptablex is the number five, and a number has no positions to index. Subscriptable is Python's word for "supports the square-bracket operation" — strings, lists and tuples are; numbers are not.
These two are the lucky cases: they fail immediately and point at the line. The endswith case in Section 5 is the unlucky one.
7. Predict, Run, Observe
Predict the type and length of each before reading on. Two of these raise on the length.
print(type((7)))
print(type((7,)))
print(type(("x")))
print(type(("x",)))
print(len((7,)))
print(len(("x")))
print(len((7, 8)))
print(len(()))The verified output:
<class 'int'>
<class 'tuple'>
<class 'str'>
<class 'tuple'>
1
1
2
0Nothing raised after all — and the sixth line is the one to look at. len(("x")) is 1, because ("x") is the one-character string "x", whose length is 1. It gives the same answer a one-element tuple would, for a completely different reason.
That is the chapter in one line: the answer looking right is not evidence that the value is right. If you need certainty, check the type, not the length.
8. Exercises
Exercise 1 — type them all. Without running anything, give the type of each, then verify:
(a) (9) (b) (9,) (c) 9, (d) () (e) ("a", "b") (f) ("a",) (g) ("a")
Exercise 2 — fix the record. A colleague wants a tuple holding one signal name and writes sig = ("clk"). Say what they actually have, what len(sig) returns and why that is misleading, and give the corrected line.
Exercise 3 — predict then verify. Run each and write down the exact result. One raises; name the error and explain it in one sentence:
print((1, 2) + (3,))print((1, 2) + (3))Exercise 4 — the silent one. Explain in three sentences why path.endswith((".v")) does not raise, what it actually checks, and how it differs from path.endswith((".v",)).
Exercise 5 — detect it. You are handed a name group and told it is a group of file extensions. Write the two lines you would run to find out whether it really is, and say what each would show if somebody had forgotten the comma.
Exercise 6 — trailing commas. Predict len((1, 2, 3,)), then verify, and say in one sentence why the trailing comma does not add an element.
Exercise 7 — explain the rule. In two sentences, explain why (5) is not a tuple, referring to what the parentheses are actually doing. Do not use the word "exception".
9. Summary
(5)is the number five. Parentheses around a single value group it; they do not create a tuple.(5,)is a tuple of one element. The trailing comma is what creates it.- This is not a special rule — it is Chapter 6.1's rule applied to one element: the comma makes the tuple.
- The parentheses are optional even here.
5,is a one-element tuple. len()is the fastest diagnostic, but it is not conclusive:len(("x"))is also 1, because the string has one character.- Check the type when you need certainty.
- Two elements and up need no trailing comma, and a trailing comma on a longer tuple adds nothing.
- The dangerous case is a one-option group.
endswith((".v"))is a plain string, not a group, and it silently checks something different. No error is possible, because the expression is valid. (1, 2) + (3)raisesTypeError: can only concatenate tuple (not "int") to tuple, and(5)[0]raisesTypeError: 'int' object is not subscriptable.
The next chapter reaches into a tuple, using position rules you already know.
Where this fits
Part of the Python curriculum.
