Lists, Tuples, Dicts, Sets & Gotchas
The four built-in collections and the gotchas GATE loves: append vs extend vs +, and aliasing vs copying a list.
What you'll learn
- Lists are mutable and ordered; tuples are immutable; dicts map keys to values; sets hold unique unordered items
- append adds ONE element (even a whole list); extend adds each element; + builds a new list
- Aliasing (b = a) shares one object; copying (b = a[:]) makes an independent list
- Predicting list state after a real 2025 append/extend output question
Before you start
Last lesson closed on a sharp question: write b = a for a list a, and do you get a
brand-new list, or merely a new name for the old one? Hold onto that question — its answer
surprises people, and it is one of the two behaviours GATE leans on hardest in this whole topic.
Python gives you four built-in collections, and the exam’s favourite traps are both about
what changed and what did not: the difference between append, extend, and +, and the
difference between aliasing a list and copying it. Get those two right and most
collection output questions simply fall open. Neither is an exam artefact, either — “I changed
one list and another one mutated too” is among the most common real bugs in day-to-day
data-wrangling code.
The four collections
- List
[1, 2, 3]— mutable, ordered, indexable. The workhorse. - Tuple
(1, 2, 3)— like a list but immutable: you cannot change it after creation. Because it is fixed, it can serve as a dict key. - Dict
`{'a': 1, 'b': 2}`— maps unique keys to values; you look items up by key. Assigning an existing key overwrites it (solendoes not grow). - Set
`{1, 2, 3}`— an unordered collection of unique items. Duplicates collapse, so`{1, 2, 2, 3}`has size 3.
append vs extend vs + — one element, or each element?
This is the single most-tested list distinction. Given a list A and another list B:
A.append(x)addsxas one element — even ifxis itself a list, it goes in nested. Length grows by exactly 1.A.extend(B)adds each element ofBone by one. Length grows bylen(B).A + Bbuilds and returns a new list; it does not changeA(you must writeA = A + Bto keep the result).
append and extend mutate the list in place and return None; + returns a fresh list and
leaves both operands alone. Seeing the three run side by side makes the distinction concrete:
A = [1, 2, 3]; B = [4, 5, 6]
A.append(B)
print("append:", A, "| len:", len(A))
A = [1, 2, 3]; B = [4, 5, 6]
A.extend(B)
print("extend:", A, "| len:", len(A))
A = [1, 2, 3]; B = [4, 5, 6]
C = A + B
print("plus :", "A =", A, "| C =", C)
prints, exactly:
append: [1, 2, 3, [4, 5, 6]] | len: 4
extend: [1, 2, 3, 4, 5, 6] | len: 6
plus : A = [1, 2, 3] | C = [1, 2, 3, 4, 5, 6]
append made a length-4 list with B buried inside it; only extend produced the flat
six-item list; and + left A untouched while building C.
Aliasing vs copying
Now back to the question from last lesson. Assigning a list with = does not copy it —
both names point at the same object, like two nicknames for one person. Mutating through
one name is therefore visible through the other:
a = [1, 2, 3]
b = a # ALIAS: b and a are the same list object
b.append(4)
print("a:", a) # a changed too!
a = [1, 2, 3]
c = a[:] # COPY: a[:] (or list(a)) builds a new, independent list
c.append(4)
print("a:", a, "| c:", c)
prints, exactly:
a: [1, 2, 3, 4]
a: [1, 2, 3] | c: [1, 2, 3, 4]
So b = a shares one list — appending through b changes a too. To get an independent
copy, slice it (a[:]), rebuild it (list(a)), or call a.copy(). That is the answer to last
lesson’s question: b = a gives you a second name, not a second list.
How GATE asks this
A pure predict-the-output MCQ: a snippet builds a list, calls append/extend or aliases
it, and you pick the final value (or its length) from four options. The distractors are exactly
the other operations’ results — so the question is really checking whether you know that
append nests, extend flattens, + is non-mutating, and = aliases. This appeared in
GATE DA 2025.
Worked example — a real 2025 question
Start with
A = [1, 2, 3]andB = [4, 5, 6]. Which single operation makesAequal to[1, 2, 3, 4, 5, 6]?
Check each against what you just traced:
A.append(B)→Abecomes[1, 2, 3, [4, 5, 6]].Bis added as one element, producing a nested list of length 4. Not equal.A.extend(B)→Abecomes[1, 2, 3, 4, 5, 6]. Each of4, 5, 6is appended in turn, length 6. This is the answer.A + B→ evaluates to[1, 2, 3, 4, 5, 6]but does not changeA;Ais still[1, 2, 3]afterward (unless you reassign withA = A + B).
So only A.extend(B) mutates A into [1, 2, 3, 4, 5, 6]. This is GATE DA 2025’s
list-operation question — and the three distractors are precisely the other operations’ results.
A question to carry forward
That aliasing rule has a sting in its tail. It is not really about the = sign at all — it is
about any two names reaching the same list. And the most common way a second name appears is
when you pass a list into a function: the function’s parameter becomes another alias for the
caller’s list, so a mutation inside the function leaks back out. That hints at a bigger topic.
Here is the thread onward: when you call a function, what names can it actually see and change —
and is there a notorious trap waiting when a function’s default argument is itself a mutable
list that quietly survives from one call to the next?
In one breath
- Four collections: list (mutable, ordered), tuple (immutable, hashable → dict key), dict (unique keys → values), set (unique, unordered — duplicates collapse).
- append vs extend vs +:
append(B)addsBas one nested element (len +1);extend(B)adds each element (len +len(B));A + Breturns a new list,Aunchanged. append/extendmutate in place and returnNone;+returns a fresh list.- Aliasing:
b = ashares one object — mutatingbchangesa. Copy witha[:],list(a), ora.copy(). - The alias rule fires for function arguments too: passing a list lets the function mutate the caller’s list.
Practice
Quick check
Practice this in an interview
All questionsComprehensions are syntactic sugar for building a new collection by iterating over an iterable and optionally filtering elements. They are faster than equivalent for-loops because the iteration runs at the C level inside the interpreter. Avoid them when the expression is too complex to read at a glance — a plain loop with descriptive variable names is preferable.
Choose a list when order matters and you need indexed access or duplicates. Choose a dict when you need to map keys to values and look up by key in O(1). Choose a set when you need uniqueness, fast membership testing, or set-algebra operations. Getting this choice wrong usually means either incorrect results (keeping duplicates when you needed uniqueness) or avoidable O(n) lookups.
Lists are mutable sequences; tuples are immutable. Use a tuple when the collection of items is fixed by meaning — coordinates, RGB values, function return values — and a list when the collection will grow, shrink, or be modified in place. Immutability also makes tuples hashable, so they can serve as dict keys or set members.
Python sets support union, intersection, difference, and symmetric difference as both operators and methods, all running in O(min(m,n)) to O(m+n) time. They are useful for deduplication, membership testing in large collections, and computing overlaps between datasets — operations that would be expensive with lists.