Previous | Tutorial index | Next
Lists and tuples are two of the most fundamental sequence types in Python. They both store ordered collections of items, support indexing and slicing, and can contain any Python objects—numbers, strings, even other sequences. However, their single most important difference—mutability—shapes how, when, and why you use each. This tutorial dives deep into both types, covers their methods and built‑in functions, and provides clear guidelines on choosing the right tool for the job.
A list is an ordered, mutable collection of items. You create a list using square brackets [], with items separated by commas.
empty_list = []
numbers = [1, 2, 3, 4]
mixed = [10, "hello", 3.14, [5, 6]] # lists can hold any types
You can also create a list using the list() constructor from any iterable:
letters = list("abc") # ['a', 'b', 'c']
ranged = list(range(5)) # [0, 1, 2, 3, 4]
As sequences, lists support the same indexing and slicing operations as strings:
0 for first, len(list)-1 for last.-1 for last, -2 for second last, etc.list[start:stop:step] returns a new list (shallow copy).nums = [10, 20, 30, 40, 50]
print(nums[1]) # 20
print(nums[-1]) # 50
print(nums[1:4]) # [20, 30, 40]
print(nums[::2]) # [10, 30, 50]
print(nums[::-1]) # [50, 40, 30, 20, 10] (reverse)
Unlike strings and tuples, lists are mutable – you can change, add, or remove elements after creation.
nums[0] = 99
print(nums) # [99, 20, 30, 40, 50]
append(x) – adds x to the end.extend(iterable) – appends all elements from the iterable.insert(i, x) – inserts x at index i (shifts subsequent elements right).nums.append(60) # [99, 20, 30, 40, 50, 60]
nums.extend([70, 80]) # [99, 20, 30, 40, 50, 60, 70, 80]
nums.insert(2, 25) # [99, 20, 25, 30, 40, 50, 60, 70, 80]
pop(i) – removes and returns the element at index i (default last).remove(x) – removes the first occurrence of x (raises ValueError if not found).clear() – removes all elements.last = nums.pop() # 80, nums becomes [99, 20, 25, 30, 40, 50, 60, 70]
second = nums.pop(1) # 20, nums becomes [99, 25, 30, 40, 50, 60, 70]
nums.remove(25) # [99, 30, 40, 50, 60, 70]
nums.clear() # []
index(x[, start[, end]]) – returns first index of x (raises ValueError if absent).count(x) – returns number of occurrences.sort(key=None, reverse=False) – sorts the list in place.reverse() – reverses the list in place.copy() – returns a shallow copy (equivalent to list[:]).letters = ['b', 'a', 'd', 'c']
letters.sort() # ['a', 'b', 'c', 'd']
letters.reverse() # ['d', 'c', 'b', 'a']
Important:
sort()andreverse()modify the list and returnNone. They are in‑place methods.
Like all sequences, lists support + and *:
a = [1, 2]
b = [3, 4]
print(a + b) # [1, 2, 3, 4]
print(a * 3) # [1, 2, 1, 2, 1, 2]
A tuple is an ordered, immutable collection of items. You create a tuple using parentheses (), with items separated by commas.
empty_tuple = ()
single = (42,) # note the trailing comma – without it, it's just the integer 42
numbers = (1, 2, 3)
mixed = (10, "hello", 3.14)
You can also use the tuple() constructor from any iterable:
t = tuple([1, 2, 3]) # (1, 2, 3)
Tuples support the same indexing and slicing as lists (and they return new tuples).
t = (10, 20, 30, 40)
print(t[1]) # 20
print(t[-1]) # 40
print(t[1:3]) # (20, 30)
Once a tuple is created, you cannot change, add, or remove elements. Any attempt results in a TypeError.
t = (1, 2, 3)
# t[0] = 99 # TypeError: 'tuple' object does not support item assignment
# t.append(4) # AttributeError: 'tuple' object has no attribute 'append'
However, if a tuple contains a mutable object (like a list), that object itself can be modified – but the tuple still holds the same reference.
t = (1, [2, 3], 4)
t[1].append(99) # allowed – the list is mutated
print(t) # (1, [2, 3, 99], 4)
Tuples have only two methods (because they are immutable):
count(x) – returns the number of occurrences of x.index(x) – returns the first index of x (raises ValueError if not found).t = (1, 2, 2, 3)
print(t.count(2)) # 2
print(t.index(3)) # 3
Like lists, tuples support + and *:
a = (1, 2)
b = (3, 4)
print(a + b) # (1, 2, 3, 4)
print(a * 3) # (1, 2, 1, 2, 1, 2)
| Feature | List | Tuple |
|---|---|---|
| Mutability | Mutable | Immutable |
| Syntax | [1, 2, 3] |
(1, 2, 3) |
| Methods | Many (append, extend, insert, remove, pop, sort, reverse, clear, copy, index, count) | Few (count, index) |
| Performance | Slightly slower, more memory | Slightly faster, less memory |
| Hashable? | No (cannot be used as dict keys) | Yes (if all elements are hashable) |
| Use Cases | Dynamic collections, homogenous data | Fixed collections, heterogenous data (like a record) |
| Iteration | Fast | Slightly faster |
Memory and Performance: Tuples are more memory‑efficient because they don't need overallocation for resizing. They are also slightly faster to access.
Many built‑in functions work on both:
len(seq) – number of elements.min(seq) – smallest element (requires comparable types).max(seq) – largest element.sum(seq) – sum of elements (for numbers).sorted(seq) – returns a new sorted list (does not modify the original).any(seq), all(seq) – boolean checks.nums = [3, 1, 4, 2]
print(len(nums)) # 4
print(min(nums)) # 1
print(max(nums)) # 4
print(sum(nums)) # 10
print(sorted(nums)) # [1, 2, 3, 4] (new list)
print(sorted(nums, reverse=True)) # [4, 3, 2, 1]
One of the most elegant features of tuples is unpacking – assigning each element to a variable.
point = (3, 5)
x, y = point # x=3, y=5
print(x, y)
You can use * to capture remaining elements (Python 3+):
first, *rest = (1, 2, 3, 4) # first=1, rest=[2,3,4]
A concise way to create lists from iterables:
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
even = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
list(tuple) – converts a tuple to a list.tuple(list) – converts a list to a tuple.t = (1, 2, 3)
lst = list(t) # [1, 2, 3]
t2 = tuple(lst) # (1, 2, 3)
Both can contain other sequences:
matrix = [[1, 2], [3, 4]] # list of lists
coordinates = ((0,0), (1,1), (2,2)) # tuple of tuples
Access nested elements with repeated indexing:
print(matrix[0][1]) # 2
Use lists when:
Use tuples when:
return a, b – actually returns a tuple).(name, age, city)).General advice: If in doubt, start with a list – you can always convert to a tuple later. But if you are sure the data will never change, a tuple is a good choice.
Which of the following correctly creates a tuple with a single element 42?
(42)[42](42,){42}What is the output of the following code?
lst = [1, 2, 3]
lst.append([4, 5])
print(len(lst))
2345True or False: Tuples are immutable, meaning you cannot change any element, even if the element itself is a mutable object.
Which method adds an element at the end of a list?
insert()extend()append()push()What does [1, 2, 3].pop(1) return?
123[1, 3]Which of the following can be used as a dictionary key?
[1, 2](1, 2){1, 2}What is the result of list("hello")?
['h', 'e', 'l', 'l', 'o']['hello']('h', 'e', 'l', 'l', 'o')"hello"Given t = (5, 2, 5, 1), what does t.count(5) return?
1230Which method sorts a list in place?
sorted()sort()order()reverse()What is the output of [1, 2] * 3?
[1, 2, 1, 2, 1, 2][3, 6][1, 2, 3]TypeErrorExercise 1: List Operations
Start with numbers = [5, 3, 8, 1, 9, 2]. Perform the following operations in order:
7 to the end.4 at index 2.1.Exercise 2: Tuple Packing and Unpacking
Write a function get_user_info() that returns a tuple (name, age, city). Then, call it and unpack the values. Create a second tuple (name, age) by slicing the first tuple. Print both.
Exercise 3: Count Occurrences
Given data = [1, 2, 3, 2, 4, 2, 5], write a program that:
2 appears using the count() method.4 using index().in, check if 3 is in the list (you can use index() with a try/except, or loop).Exercise 4: List of Tuples
Create a list of tuples representing students: [("Alice", 85), ("Bob", 92), ("Charlie", 78)].
"Alice: 85".max() with a key.Exercise 5: Matrix Transposition
Given a 2D list (matrix) matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], write code to produce its transpose:
[[1, 4, 7],
[2, 5, 8],
[3, 6, 9]]
Use list comprehensions and/or loops.
1. Inventory System
Design a simple inventory system using a list of tuples. Each tuple represents an item: (item_id, name, quantity, price). Start with:
inventory = [
(1, "Laptop", 5, 999.99),
(2, "Mouse", 20, 19.99),
(3, "Keyboard", 10, 49.99)
]
Write functions to:
add_item(inventory, id, name, qty, price) – appends a new tuple.update_quantity(inventory, id, new_qty) – finds the item by id and replaces the quantity (you'll need to rebuild the tuple).remove_item(inventory, id) – removes the item by id.total_value(inventory) – returns the total value of all items (qty * price).display_inventory(inventory) – prints a nicely formatted table.def update_quantity(inventory, id, new_qty): for i, item in enumerate(inventory): if item[0] == id: inventory[i] = (item[0], item[1], new_qty, item[3]) return raise ValueError("Item not found")
def remove_item(inventory, id): for i, item in enumerate(inventory): if item[0] == id: del inventory[i] return raise ValueError("Item not found")
def total_value(inventory): return sum(item[2] * item[3] for item in inventory)
def display_inventory(inventory): print(f"{'ID':>3} {'Name':<10} {'Qty':>4} {'Price':>8} {'Total':>8}") for item in inventory: total = item[2] * item[3] print(f"{item[0]:>3} {item[1]:<10} {item[2]:>4} {item[3]:>8.2f} {total:>8.2f}")
</details>
**2. Unique Elements**
Write a function `unique_preserve_order(lst)` that returns a new list with duplicate elements removed, preserving the original order of first occurrence. Use only list methods (no sets allowed). Test with `[1, 2, 2, 3, 1, 4]` → `[1, 2, 3, 4]`.
<details><summary>Sample Answer</summary>
```python
def unique_preserve_order(lst):
result = []
for item in lst:
if item not in result:
result.append(item)
return result
3. Rotate a List
Write a function rotate(lst, k) that rotates the list to the right by k positions (modulo length). Example: rotate([1,2,3,4,5], 2) → [4,5,1,2,3]. Use slicing and concatenation. Do not use loops. Also, handle negative k (rotate left) and k=0.
4. Tuple as a Record – Student Grades
Write a program that reads student data from a multi‑line string (simulating a CSV file) where each line is "Name,Math,Science,English". Use split() to parse each line into a tuple (name, math, science, english) (convert scores to ints). Store all tuples in a list. Then:
(name, average) sorted by average descending.for name, math, sci, eng in students: avg = (math + sci + eng) / 3 print(f"{name}: {avg:.2f}")
highest = max(students, key=lambda s: (s[1]+s[2]+s[3])/3) print("Highest avg:", highest[0])
math_avg = sum(s[1] for s in students) / len(students) sci_avg = sum(s[2] for s in students) / len(students) eng_avg = sum(s[3] for s in students) / len(students) print(f"Math avg: {math_avg:.2f}, Science: {sci_avg:.2f}, English: {eng_avg:.2f}")
sorted_by_avg = sorted([(s[0], (s[1]+s[2]+s[3])/3) for s in students], key=lambda x: x[1], reverse=True) print(sorted_by_avg)
</details>
#### Essay Questions
**5. Deep vs Shallow Copy**
This question tests understanding of mutability and copying.
Given:
```python
original = [1, [2, 3], 4]
copy1 by slicing (original[:]).copy2 using list(original).copy3 using original.copy().original[1][0] = 99. What happens to copy1, copy2, copy3? Why?original[0] = 100. What happens to the copies?copy module's deepcopy and show how it behaves.original[1][0] = 99 print(copy1, copy2, copy3) # all show [1, [99, 3], 4] because the nested list is shared
original[0] = 100 print(copy1, copy2, copy3) # copies remain unchanged at index 0
deep = copy.deepcopy(original) original[1][0] = 42 print(deep) # [1, [99, 3], 4] – unaffected because deep copy is independent
Shallow copy copies the top-level container but shares nested objects. Deep copy recursively copies everything.
</details>
### Homework Hints
- **Q1**: For `update_quantity`, you need to find the index, then create a new tuple: `new_item = (id, name, new_qty, price)` and assign to `inventory[index]`.
- **Q2**: Use a new list and check `if item not in new_list`.
- **Q3**: `k = k % len(lst)`; then `lst[-k:] + lst[:-k]` for right rotation; for left, `lst[k:] + lst[:k]`.
- **Q4**: Parse lines, convert scores to int, use tuple of 4 elements.
- **Q5**: Slicing creates a shallow copy; modifying nested list affects all shallow copies. Only `deepcopy` creates independent copies.
## Summary
In this tutorial, you have learned:
- The syntax and core properties of lists (mutable) and tuples (immutable).
- The rich set of list methods and the limited but useful tuple methods.
- Common sequence operations and built‑in functions.
- Advanced concepts like tuple unpacking and list comprehensions.
- Practical guidelines on when to use each type.
With this knowledge, you are now equipped to choose the appropriate sequence type for your data, write more efficient and readable code, and avoid common pitfalls related to mutability.
**Next Steps**: In Tutorial 5, we will explore **Dictionaries and Sets** – powerful data structures for key‑value mappings and unique collections.
*Happy coding with lists and tuples!*
<!-- tutorial-navigation:start -->
[Previous](t-3.html) | [Tutorial index]() | | [Next](t-5.html)
<!-- tutorial-navigation:end -->