Choose mutable and immutable sequences, copy and sort safely, unpack values, and transform data with readable comprehensions.
A sequence stores values in a deliberate order. Python lists and tuples support the same core operations—iteration, indexing, slicing, membership, and unpacking—but they communicate different intentions. Lists are mutable collections that can grow or change; tuples are immutable sequences suited to fixed records and stable groups of values.
Learning goals
Choose a list for a changing collection and a tuple for a fixed record.
Read and update values with indexes while handling boundaries safely.
Use slices and understand that they make shallow copies.
Distinguish aliasing from copying, including nested mutable values.
Use list methods, sorted, unpacking, zip, and comprehensions deliberately.
Recognize when a generator expression or explicit loop is clearer than a list comprehension.
Choose lists and tuples by meaning
A list uses square brackets and can be changed after creation. A tuple usually uses parentheses and cannot have its positions added, removed, or replaced. Both preserve order and may contain values of different types, although a collection is usually easier to reason about when its items share one role.
Immutability applies to the tuple's references, not recursively to every object inside it. A tuple can contain a list, and that inner list can still change. Use a tuple when the number and meaning of positions are fixed; use a data class or named structure when readers would otherwise need to memorize what each position means.
Access positions with indexes
Indexes begin at 0. The first item is sequence[0], and the final item is sequence[-1]. A negative index counts backward from the end. Accessing a position outside the sequence raises IndexError, which is useful evidence that the program's assumptions and the available data disagree.
A slice uses start:stop:step. The start is included and the stop is excluded, matching range. Omitted boundaries extend to the beginning or end. A positive step moves forward; a negative step moves backward. Slicing a list creates a new outer list, so later replacement or append operations on that outer list do not affect the original.
Assignment does not copy a list. It binds another name to the same object, so a mutation through either name is visible through both. Use list(source), source.copy(), or source[:] for a shallow copy. A shallow copy duplicates only the outer container; nested lists and other mutable items remain shared.
PYTHON · EDITABLE
original =[["Python",3],["CSS",5]]
alias = original
shallow = original.copy()
alias.append(["HTML",4])
shallow[0][1]=10print(original)# append and nested change are visibleprint(shallow)# separate outer list, shared inner lists
Mutate lists with explicit methods
append adds one item, extend adds every item from another iterable, insert adds at a position, remove deletes the first equal value, and pop removes and returns an item. clear removes all items. Most mutating list methods return None so you do not mistake the mutation for a new list.
PYTHON · EDITABLE
queue =["Amina","Youssef"]
queue.append("Salma")
queue.extend(["Omar","Lina"])
first = queue.pop(0)print("Serving:", first)print("Waiting:", queue)# Wrong: queue = queue.append("Nora")# append changes the list and returns None.
Search and summarize sequences
Use in and not in for membership, count for the number of equal occurrences, and index when a missing value should raise ValueError. Built-ins such as len, min, max, sum, any, and all summarize compatible values without a manual loop. Avoid repeatedly searching a large list when a set would better represent fast membership.
PYTHON · EDITABLE
scores =[84,61,93,84]print(84in scores)print(scores.count(84))print(min(scores),max(scores),sum(scores))print(any(score >=90for score in scores))print(all(0<= score <=100for score in scores))
Sort in place or create a sorted result
list.sort changes one list and returns None. sorted accepts any iterable and returns a new list. Both support reverse and a key function that computes the value used for comparison. Prefer a key over reshaping the data merely to influence order.
PYTHON · EDITABLE
courses =[("Python",10),("HTML",19),("CSS",18),]
by_lesson_count =sorted(courses, key=lambda course: course[1])print(by_lesson_count)print(courses)# original order remains unchanged
courses.sort(key=lambda course: course[0].lower())print(courses)
A small lambda is appropriate for a short key expression. If the rule needs validation, branches, reuse, or explanation, define a named function instead. Python's sort is stable: values with equal keys preserve their earlier relative order, which supports sorting by multiple criteria in deliberate stages.
Unpack fixed and variable-length sequences
Unpacking binds positions to names and verifies the expected shape. The number of names must match unless one target is starred. A starred target receives the remaining values as a new list. Use an underscore for a deliberately ignored value, while remembering that it is still an ordinary variable name.
PYTHON · EDITABLE
course =("Python","beginner",10)
name, level, lesson_count = course
first,*middle, last =[62,74,81,93,88]print(name, lesson_count)print(first, middle, last)# Swap without a temporary variable:
left, right ="A","B"
left, right = right, left
Combine aligned sequences with zip
zip pairs items from multiple iterables position by position and stops when the shortest iterable ends. That silent truncation may be correct, or it may hide mismatched data. On supported Python versions, strict=True raises ValueError when lengths differ and makes the equal-length contract explicit.
A list comprehension builds a new list by evaluating one expression for each item, optionally keeping only items that satisfy a condition. Read it as: produce this expression, for each item, if this filter passes. It is ideal for one clear transformation and one simple filter.
PYTHON · EDITABLE
raw_scores =[84,-1,61,105,93]
valid_scores =[
score
for score in raw_scores
if0<= score <=100]
percent_labels =[f"{score}%"for score in valid_scores]print(valid_scores)print(percent_labels)
Use an explicit loop when the transformation has several branches, needs logging, mutates other state, or would require a deeply nested comprehension. Comprehensions should make the rule easier to see, not merely use fewer lines.
Avoid unnecessary lists with generator expressions
A generator expression uses parentheses and produces values lazily as a consumer asks for them. It is useful when sum, any, all, min, max, or another consumer can process values one at a time. A generator is consumed during iteration and does not support indexing like a list.
PYTHON · EDITABLE
scores =[84,61,93,70]
passing_total =sum(score for score in scores if score >=70)
has_excellent =any(score >=90for score in scores)print(passing_total)print(has_excellent)
Build a practical performance model
Index access at a known list position is constant-time. Appending is usually constant-time. Searching with in, count, index, or remove may inspect every item. Inserting or removing near the beginning shifts later positions. Sorting grows more expensive than a single pass. These facts matter when data becomes large, but clear and correct code should come before speculative optimization.
Use a list when order and duplicates matter.
Use a tuple for a fixed positional record or stable group.
Use a set when uniqueness and repeated membership checks are the main job.
Use a dictionary when values need meaningful keys.
Measure real workloads before replacing a clear representation for speed.
Common sequence mistakes
Assuming assignment copies a list.
Forgetting that a shallow copy still shares nested mutable objects.
Assigning the result of append or sort and receiving None.
Reading an index from an empty or shorter-than-expected sequence.
Forgetting that slice and range stop values are exclusive.
Using a comprehension with several effects or branches that obscure the rule.
Using zip without deciding whether unequal lengths should be accepted.
Build a study-session report
Use the notebook below to preserve the raw durations, filter invalid values, sort a new list, unpack the shortest and longest sessions, and calculate a rounded average. Add another invalid duration and confirm that the original data remains unchanged.
Predict each intermediate list before running the code.
Explain why sorted is used instead of list.sort.
Confirm that raw_durations and valid_durations are different list objects.
Replace the comprehension with an explicit loop and compare readability.
Add a guard for the case where no valid sessions remain.
Lesson review
You can now choose between lists and tuples, access and slice ordered data, reason about aliases and shallow copies, mutate lists deliberately, sort and unpack values, combine aligned data, and write readable comprehensions or generator expressions. Complete the quick checks and scored quiz before continuing to mappings and sets.
RUNNABLE PYTHON NOTEBOOK
Build a study-session report
Clean and summarize ordered data while preserving the original list. Change the inputs, predict every intermediate value, and test the empty-valid-results guard.
defvalid_durations(raw_durations:list[int])->list[int]:return[
minutes
for minutes in raw_durations
if1<= minutes <=240]defsession_summary(raw_durations:list[int])->str:
cleaned = valid_durations(raw_durations)ifnot cleaned:return"No valid study sessions"
ordered =sorted(cleaned)
shortest,*middle, longest = ordered
average =sum(ordered)/len(ordered)return(f"{len(ordered)} sessions | "f"shortest {shortest} min | "f"longest {longest} min | "f"average {average:.1f} min | "f"middle values {middle}")
raw_durations =[45,-5,90,30,300,60]
original_snapshot = raw_durations.copy()print(session_summary(raw_durations))print("Original preserved:", raw_durations == original_snapshot)print("Valid values:", valid_durations(raw_durations))assert valid_durations([30,0,241,60])==[30,60]assert session_summary([0,-10,500])=="No valid study sessions"
OUTPUT
Press Run code to execute this Python program in your browser.
QUICK CHECK
Test what you learned
Which method adds one item to the end of a list?
QUICK CHECK
Test what you learned
Which function returns a new sorted list without changing the original iterable?
QUICK CHECK
Test what you learned
What kind of copy duplicates the outer list but keeps nested objects shared?
KNOWLEDGE QUIZ
Check your sequence mental model
Choose an answer for every question, then check the explanations. The goal is not to memorize syntax; it is to predict what Python will do and why.
Lists, Tuples, and Comprehensions Tutorial | SovranCode