skip to content

Tuple Packing and Value Semantics

Commas create tuples, not parentheses, which is why (x) is just x and (x,) is a one-element tuple. Tuples are the immutable record Python uses for multiple returns and composite dict keys.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

4

Why is (5) just an int in Python while (5,) is a one-element tuple?

level: juniorimportance: must knowfreq 74%

answer

  1. Punctuation, not brackets
  2. Which character separates the items?
  3. Brackets only change precedence
  4. One item still needs its separator
  5. () is the single exception

basics

~20 s

In Python the comma builds a tuple, not the parentheses. (5) is the integer 5 inside redundant grouping brackets, while the trailing comma in (5,) makes a one-element tuple. Empty () is the only exception.

solid answer

~40 s

The tuple constructor in Python's grammar is the **comma**; parentheses only group an expression, exactly as they do in `(2 + 3) * 4`. So `(5)` evaluates to the int `5`, `5,` and `(5,)` both build a one-element tuple, and `1, 2, 3` is a three-element tuple with no brackets at all. The one bracket-only form is `()`, the empty tuple, since there is nothing for a comma to separate. This is why `return x, y` returns a single tuple, why the repr of a one-element tuple keeps its trailing comma as `(5,)`, and why a stray comma at the end of an assignment silently wraps the value: `total = compute(),` binds a tuple, and the bug usually surfaces later as a `TypeError` on arithmetic or a JSON array where a number was expected.

code

python · 6 lines
python
x = (5)
one = (5,)
also_one = 5,
empty = ()
print(type(x).__name__, type(one).__name__, type(also_one).__name__)
print(len(one), one, empty)

go deeper

for a junior

Recall the rule as a sentence: commas make tuples, brackets only group. Be able to say instantly what type((5)) and type((5,)) are, and to write an empty tuple.

for a middle

Explain the mechanics: an expression list separated by commas is a tuple, parentheses are the grouping operator, and () is the bracket-only exception. Show where brackets become necessary, such as passing a tuple as one call argument.

for a senior

Be ready to diagnose the trailing-comma bug from downstream symptoms - a TypeError on concatenation, or a serializer emitting a one-item array - and to say how you would catch it in review or with a type checker rather than at runtime.

for a principal

Own the readability position: where a bare comma tuple is fine in your codebase and where you require brackets or a named record type instead, so that a one-element tuple never depends on a character a reviewer can miss.

### The comma is the constructor Most people learn tuples as "the thing in round brackets", which is a mis-read of the grammar. In Python an *expression list* is one or more expressions separated by commas, and **it is the comma that makes the value a tuple**. Parentheses are a grouping operator: they change precedence, nothing more. Two consequences follow immediately. First, `(5)` is not a tuple. The parser sees a parenthesised expression whose value is the integer `5`, exactly as in `(2 + 3) * 4`. `type((5))` is `int`, and `len((5))` raises `TypeError` because integers have no length. Second, brackets are optional wherever the comma is unambiguous: ```python point = 3, 4 # a 2-tuple, no brackets print(type(point)) # <class 'tuple'> ``` ### The singleton trap A one-element tuple therefore needs a comma of its own, and it must be a *trailing* comma: `(5,)` or bare `5,`. Python's repr echoes that comma back at you — a value printed as `(5,)` in a log or a traceback is a one-tuple, and that trailing comma is the fastest tell when you are reading someone else's output. You can also build one without any comma at all by calling the type on an iterable, as in `tuple([5])`, which is what you reach for when the element comes out of a variable and a literal would be awkward. The empty tuple `()` is the sole bracket-only form: there is no element for a comma to follow, so the brackets carry the whole meaning. As an implementation detail CPython reuses one cached empty-tuple object, so every `()` you write is the same object — pleasant, but not something to build behaviour on. ### Where the trap actually bites The classic production bug is a stray trailing comma: ```python total = sum(amounts), # note the comma ``` `total` is now a one-element tuple. Nothing fails at that line. It fails later, when something adds to it (`can only concatenate tuple (not "int") to tuple`), when a comparison raises, or — worst — when it does not fail at all and a serializer emits `[42]` instead of `42`, because `json.dumps` renders a tuple as a JSON array. The mirror-image mistake is writing `(5)` and expecting a container: `for x in (5)` raises `TypeError: 'int' object is not iterable`, while `for x in (5,)` loops once. A related everyday case is passing a tuple as a *single* argument. `f(1, 2)` passes two arguments; `f((1, 2))` passes one tuple. Here the parentheses are load-bearing not because they build the tuple — the inner comma does that — but because without them the comma would be read as the call's argument separator. The same reasoning explains why you sometimes need brackets inside a list display: `[(1, 2), (3, 4)]` is two tuples, while `[1, 2, 3, 4]` is four items. ### Two more places the rule shows up Old-style percent formatting takes a *tuple* of values on the right, which is why `"%s" % (value,)` is written with a trailing comma: `"%s" % value` happens to work for a plain integer, then breaks the day `value` is itself a tuple, because the formatter spreads it across the placeholders. The one-tuple form makes the intent explicit and never breaks. The other is the constructor. `tuple(x)` does not wrap `x`; it *iterates* it. So `tuple([5])` is `(5,)` as you would hope, but `tuple('ab')` is `('a', 'b')` and `tuple(5)` raises `TypeError` because an integer is not iterable. When you want a one-element tuple around an arbitrary value, the comma form `(value,)` — or `tuple([value])` — is the only one that is always right. ### Multiple return values are one tuple `return numerator, denominator` does not return two things. It packs one tuple and returns it; the caller conventionally spreads it back out into names. That is the whole mechanism behind Python's "multiple return values", and it is why a function that returns a pair can be handed straight to anything that accepts a sequence, and why `divmod(7, 2)` — which genuinely produces two results — hands you `(3, 1)`. ### One 3.14 wrinkle worth knowing Because a comma always meant "tuple", catching several exception types historically required brackets: `except (ValueError, TypeError):`. **Python 3.14 (PEP 758) relaxed this**, so `except ValueError, TypeError:` is now legal when there is no `as` clause. It is the one place in modern Python where an un-bracketed comma list is *not* a tuple expression, and it is worth naming in an interview because it shows you know the rule well enough to know its exception. Older code, and any `except` clause that binds the exception with `as`, still needs the brackets. ### The one-line summary Commas make tuples; brackets group. `(5)` is an int, `(5,)` is a tuple, `()` is empty, and a trailing comma you did not mean to type is a real bug that hides until something downstream does arithmetic.

  • When are the parentheses around a tuple actually required?
    Whenever the comma would otherwise be read as a separator in the enclosing construct: as a single argument in a call, `f((1, 2))` versus `f(1, 2)`; as an element inside a list or set display, `[(1, 2), (3, 4)]`; and around a tuple used as an operand where precedence matters. Everywhere else — assignments, `return`, a bare expression statement — the comma alone is enough.
  • How would you build a one-element tuple when the value is already in a variable?
    `(value,)` still works, but `tuple([value])` reads better when the trailing comma would be easy to miss in review, and it is what you want when the source is an arbitrary iterable. Beware `tuple(value)` with a string: it iterates the characters, so `tuple('ab')` is `('a', 'b')`, not `('ab',)`.
  • How do you write an empty tuple, and why does it not need a comma?
    `()` — or `tuple()`. It is the only tuple form the brackets alone create, because there is no element for a comma to follow. CPython reuses a single cached empty-tuple object, so every `()` is the same object, though that is an implementation detail rather than a guarantee to rely on.

Brackets are like the parentheses in arithmetic - they group, they do not create. The comma is the actual verb, the way a semicolon rather than the page makes two sentences.

saying these in an interview costs you the question

  • Says the parentheses are what create a tuple
  • Claims (5) is a one-element tuple
  • Thinks a trailing comma is purely cosmetic style
  • Believes tuples always require surrounding brackets
  • Confuses the empty tuple () with None or []
  • Expects len((5)) to return 1

context

open as a page

How does a Python function return multiple values to its caller?

level: middleimportance: must knowfreq 58%

basics

~20 s

Python functions return exactly one object. Writing return lo, hi builds a tuple - the comma, not the parentheses, creates it - so the caller gets one immutable two-field record it can hold, index, or destructure.

open as a page

How does Python compare two tuples with <, and how does that drive sorted()?

level: middleimportance: should knowfreq 52%

basics

~10 s

Tuples compare element by element: the first unequal pair decides, and a prefix is the smaller tuple. sorted() uses that ordering, so a key function returning a tuple sorts by several fields.

open as a page

Why do the dict keys (1, 'seats') and (True, 'seats') collapse into a single entry?

level: seniorimportance: nice to knowfreq 17%

basics

~20 s

Tuples compare and hash element-wise, and in Python True == 1 == 1.0 with equal hashes. The two tuples are therefore equal keys: the second write overwrites the first value, while the dict keeps the key object it stored first.

open as a page