☰ Learn Python Tutorial Menu

Tuples and Sets

Written by CSA mentors · Updated 26 Sept 2026 · 4 min read


Here's a question we ask in class. You have last month's list of 38,000 flagged customer IDs and this month's list of 40,000. Which ones are new? Most students start writing a loop inside a loop. That runs for minutes. The right answer runs in a blink, and it uses a type we haven't covered yet. Lists are flexible, but flexibility isn't always what you want. A record like a (date, amount) pair shouldn't be changed by accident, and a collection of customer IDs shouldn't hold duplicates. Python has a type for each job. The tuple (fixed, ordered) and the set (unique, unordered).

Tuples, for fixed records

A tuple is written with parentheses (or just commas). Once created it can't be changed.

islamabad = (33.6844, 73.0479)
record = ("2026-09-24", "Lahore", 2500)

print(record[1])              # Lahore
lat, lon = islamabad          # unpacking
print(lat)

record[2] = 3000              # TypeError: 'tuple' object does not support item assignment

Why would anyone want something they can't change? Three reasons.

  • Safety. A function that returns a tuple guarantees the caller won't accidentally mutate shared data.
  • Dictionary keys. Only immutable values can be dict keys, so a tuple like ("Lahore", "2026-09") works as a key and a list doesn't.
  • Meaning. A tuple says "this is one record with fixed fields". A list says "this is a sequence that may grow".

A one-element tuple needs a trailing comma, so (5,). Without it, (5) is just the number 5. This one produces a confused face every batch.

You've been using tuples already

divmod(135, 60) returns (2, 15). The swap a, b = b, a builds and unpacks a tuple. Any function that returns several values returns a tuple.

def min_max(values):
    return min(values), max(values)        # returns a tuple

lo, hi = min_max([120, 95, 140])
print(lo, hi)                              # 95 140

Named tuples

record[2] tells the reader nothing. collections.namedtuple gives the fields names while keeping tuple behaviour.

from collections import namedtuple

Sale = namedtuple("Sale", ["date", "city", "amount"])
s = Sale("2026-09-24", "Lahore", 2500)
print(s.city, s.amount)                    # Lahore 2500
print(s)                                   # Sale(date='2026-09-24', city='Lahore', amount=2500)

Sets, for unique values

Three panels comparing list, tuple and set on ordering, mutability, duplicates and typical use

A set holds each value at most once, has no order, and answers "is it in there?" almost instantly.

cities = {"Lahore", "Multan", "Lahore", "Karachi", "Multan"}
print(cities)                  # {'Karachi', 'Lahore', 'Multan'}  order may vary
print(len(cities))             # 3

cities.add("Quetta")
cities.discard("Multan")       # no error if missing (remove() would raise)
print("Lahore" in cities)      # True, and very fast even with millions of items

Careful, {} creates an empty dict, not a set. For an empty set write set().

Deduplicating a list

emails = ["a@x.pk", "b@x.pk", "a@x.pk", "c@x.pk", "b@x.pk"]
unique = list(set(emails))
print(len(emails), "->", len(unique))     # 5 -> 3

This loses the original order. When order matters, use list(dict.fromkeys(emails)), which keeps the first occurrence of each.

Set operations

OperationSyntaxQuestion it answers
Uniona | bEveryone in either list
Intersectiona & bWho is in both
Differencea - bIn a but not b
Symmetric differencea ^ bIn exactly one of them
Subseta <= bIs every a in b?
paid_2025 = {"C001", "C002", "C003", "C007"}
paid_2026 = {"C002", "C003", "C009"}

print(paid_2025 & paid_2026)   # {'C002', 'C003'}   retained customers
print(paid_2025 - paid_2026)   # {'C001', 'C007'}   churned
print(paid_2026 - paid_2025)   # {'C009'}           new
print(paid_2025 | paid_2026)   # all five

That's the answer to the opening question. Nested loops over two lists of tens of thousands of IDs take minutes. Two sets and a minus sign take milliseconds. When I review a student's churn script and see a double loop, this is the lesson I send them back to.

Which one do I use?

You needUse
Ordered items that will grow, shrink or be sortedlist
A fixed record of a few fields, or a dict keytuple
Unique values, fast "is it there?", overlaps between groupsset
Frozen set usable as a dict keyfrozenset

Two exports, one fraud desk in Karachi: The team gets this month's flagged CNICs from one system and last month's from another. Converting each export to a set and subtracting gives the newly flagged customers in one line, and & gives the repeat offenders. The same team stores each alert as a tuple (cnic, timestamp, amount) so no downstream script can quietly overwrite the amount.

Quick recap

  • Tuples are ordered and immutable. Use them for fixed records, multiple return values and dict keys.
  • namedtuple gives tuple fields readable names.
  • Sets hold unique values with no order, and in checks are near instant.
  • Set operations (&, |, -) answer overlap questions without loops.
  • set() for an empty set. {} is an empty dict.

Try these

  1. Create a tuple for a Faisalabad shipment (container number, destination port, weight in kg). Unpack it into three variables and print a sentence using them.
  2. Given two lists of student roll numbers, those who submitted assignment 1 and those who submitted assignment 2, use sets to print who submitted both, who submitted only the first, and how many submitted at least one.
  3. Remove duplicates from ["Lahore", "lahore", "LAHORE", "Multan"] ignoring case, keeping the first spelling seen.

Lesson 9 of 26

Sign in to track your progress and earn learning points for every lesson you finish.

Example

print("Hello from CSA!")

for i in range(5):
    print(i * i)
Try it Yourself »