Lists and Tuples in Python

Understanding Sequence Types

Lists and tuples are fundamental sequence data types in Python. They both store ordered collections of items, but with key differences in mutability, performance, and use cases.

Both can contain elements of different data types, including other lists or tuples, and support indexing, slicing, and iteration operations.

# Lists use square brackets
fruits = ["apple", "banana", "cherry"]

# Tuples use parentheses
coordinates = (10, 20)

# Both allow indexing
print(fruits[0])       # "apple"
print(coordinates[1])  # 20

# Both allow slicing
print(fruits[1:3])     # ["banana", "cherry"]

# Key difference: mutability
fruits[0] = "apricot"  # Valid - lists are mutable
# coordinates[0] = 30  # Invalid - tuples are immutable

Python Lists in Detail

List Creation and Basics

Lists are mutable, ordered sequences of elements. Each element can be of any data type. They're created with square brackets or the list() constructor.

# Creating lists
empty_list = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]
nested = [1, [2, 3], [4, [5, 6]]]

# List constructor
from_range = list(range(5))  # [0, 1, 2, 3, 4]
from_string = list("hello")  # ['h', 'e', 'l', 'l', 'o']
from_tuple = list((1, 2, 3))  # [1, 2, 3]

# Accessing elements
first = numbers[0]  # 1
last = numbers[-1]  # 5
middle = numbers[1:4]  # [2, 3, 4]

List Methods and Operations

Lists provide numerous methods for adding, removing, and manipulating elements.

# Adding elements
fruits = ["apple", "banana"]
fruits.append("cherry")        # Add to end: ['apple', 'banana', 'cherry']
fruits.insert(1, "orange")    # Insert at index: ['apple', 'orange', 'banana', 'cherry']
fruits.extend(["mango", "kiwi"])  # Add multiple items: ['apple', 'orange', 'banana', 'cherry', 'mango', 'kiwi']

# Removing elements
fruits.remove("banana")       # Remove by value
last_fruit = fruits.pop()     # Remove and return last item: 'kiwi'
second_fruit = fruits.pop(1)  # Remove by index: 'orange'
fruits.clear()                # Remove all items: []

# Finding elements
numbers = [10, 20, 30, 20, 40]
print(numbers.index(20))      # 1 (first occurrence)
print(numbers.count(20))      # 2 (number of occurrences)

# Sorting and reversing
names = ["Charlie", "Alice", "Bob"]
names.sort()                  # In-place sort: ['Alice', 'Bob', 'Charlie']
names.sort(reverse=True)      # Descending sort: ['Charlie', 'Bob', 'Alice']
names.reverse()               # In-place reverse: ['Alice', 'Bob', 'Charlie']

# Sorted copy
sorted_copy = sorted(names)   # Returns new list, doesn't modify original

List Slicing and Copying

Slicing lets you extract portions of a list. It's a powerful feature for manipulating sequences.

# Slicing syntax: list[start:stop:step]
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:5])     # [2, 3, 4]
print(numbers[:3])      # [0, 1, 2]
print(numbers[7:])      # [7, 8, 9]
print(numbers[1:8:2])   # [1, 3, 5, 7]
print(numbers[::-1])    # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] (reversed)

# Copying lists
shallow_copy1 = numbers[:]    # Slice copy
shallow_copy2 = numbers.copy()  # Method copy
shallow_copy3 = list(numbers)   # Constructor copy

# Deep copying (for nested lists)
import copy
nested = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(nested)

List Comprehensions

List comprehensions provide a concise way to create lists based on existing lists or other iterables.

# Basic list comprehension
squares = [x**2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# With condition
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# Nested list comprehension
matrix = [[j for j in range(3)] for i in range(3)]
print(matrix)  # [[0, 1, 2], [0, 1, 2], [0, 1, 2]]

# With if/else
values = [x if x % 2 == 0 else -x for x in range(10)]
print(values)  # [0, -1, 2, -3, 4, -5, 6, -7, 8, -9]

Python Tuples in Detail

Tuple Creation and Basics

Tuples are immutable, ordered sequences of elements. Once created, their values cannot be changed. They're created with parentheses or the tuple() constructor.

# Ways to create tuples
empty_tuple = ()
single_item = (1,)  # Note the comma! Without it, it's just an int in parentheses
coordinates = (10, 20, 30)
mixed_tuple = (1, "hello", 3.14)
from_constructor = tuple([1, 2, 3])  # (1, 2, 3)
from_string = tuple("hello")  # ('h', 'e', 'l', 'l', 'o')

# Tuple size and membership
print(len(coordinates))  # 3
print(20 in coordinates)  # True

Tuple Methods and Operations

Since tuples are immutable, they have fewer methods than lists, but they still support many operations.

# Accessing elements
coordinates = (10, 20, 30)
print(coordinates[0])    # 10
print(coordinates[-1])   # 30

# Slicing works like lists
print(coordinates[1:])   # (20, 30)

# Only two methods
values = (1, 2, 2, 3, 4, 2)
print(values.count(2))   # 3 (occurrences of 2)
print(values.index(3))   # 3 (index of first 3)

# Concatenation and repetition
tuple1 = (1, 2)
tuple2 = (3, 4)
combined = tuple1 + tuple2  # (1, 2, 3, 4)
repeated = tuple1 * 3       # (1, 2, 1, 2, 1, 2)

Tuple Unpacking

One of the most powerful features of tuples is their ability to be unpacked into multiple variables.

# Basic unpacking
point = (10, 20, 30)
x, y, z = point
print(x, y, z)  # 10 20 30

# Unpacking with rest operator (*)
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(first)   # 1
print(middle)  # [2, 3, 4]
print(last)    # 5

# Swapping values
a, b = 5, 10
a, b = b, a  # Now a is 10, b is 5

# Function returning multiple values (as a tuple)
def get_dimensions():
    return 1920, 1080  # Returns a tuple (1920, 1080)

width, height = get_dimensions()
print(f"Width: {width}, Height: {height}")  # Width: 1920, Height: 1080

Named Tuples

Named tuples enhance regular tuples by allowing you to access elements by name instead of just index.

# Named tuples from collections module
from collections import namedtuple

# Define a new type
Point = namedtuple('Point', ['x', 'y', 'z'])

# Create an instance
point = Point(10, 20, 30)

# Access by name or index
print(point.x)    # 10 (by name)
print(point[1])   # 20 (by index)
print(point._asdict())  # {'x': 10, 'y': 20, 'z': 30} (as dictionary)

# Named tuples are immutable
# point.x = 100  # AttributeError

# But you can create a new one with _replace
new_point = point._replace(x=100)
print(new_point)  # Point(x=100, y=20, z=30)

Lists vs Tuples: When to Use Which

Each has its strengths and ideal use cases. Understanding when to use each can lead to more efficient and maintainable code.

Feature List Tuple
Mutability Mutable (can change) Immutable (fixed)
Syntax Square brackets `[]` Parentheses `()`
Size Dynamic Fixed
Memory More overhead Less overhead
Performance Slower Faster
Methods Many (append, remove, etc.) Few (count, index)
Use as Dictionary Key No (not hashable) Yes (hashable)

When to Use Lists

  • When the collection needs to be modified (adding/removing elements)
  • For homogeneous collections that may grow or shrink
  • When you need to use methods like sort, reverse, etc.
  • For data that represents a collection of similar items

When to Use Tuples

  • For fixed data that shouldn't change
  • As dictionary keys (since they're hashable)
  • For heterogeneous data that forms a single logical item
  • For returning multiple values from a function
  • For data that's meant to be unpacked
# Use a list when the collection will change
shopping_cart = ["apple", "milk", "bread"]
shopping_cart.append("eggs")

# Use a tuple for fixed, structured data
person = ("John Doe", 35, "Developer")
name, age, occupation = person

# Tuples can be used as dictionary keys
locations = {
    (40.7128, -74.0060): "New York",
    (34.0522, -118.2437): "Los Angeles"
}
print(locations[(40.7128, -74.0060)])  # "New York"

# Lists cannot be used as dictionary keys
# locations[[40.7128, -74.0060]] = "New York"  # TypeError: unhashable type: 'list'

Comparison: Lists vs. Tuples

Feature Lists Tuples
Mutability Mutable (can be changed) Immutable (cannot be changed)
Syntax Square brackets [] Parentheses ()
Size More memory (resizable) Less memory (fixed size)
Memory usage Higher (extra memory for resizing) Lower (exactly what's needed)
Performance Slower Faster
Methods Many (append, extend, insert, remove...) Few (count, index)
Dict keys Cannot be dictionary keys Can be dictionary keys

Practice Exercises

Try These:

  1. Create a function that takes a list and returns a new list with unique elements of the first list.
  2. Write a Python program to find the second largest number in a list.
  3. Create a function that counts the number of elements in a list without using len().
  4. Write a function to flatten a nested list into a single list.
  5. Create a named tuple for representing a 3D point and use it to calculate the distance between two points.