A Python list is an ordered, mutable sequence written as comma-separated values between square brackets, such as [“a”, 1, 2.5]. Lists are indexed from zero, can hold items of different types, and grow or shrink as we add and remove items.
fruits = ["apple", "banana"]
fruits.append("cherry") # ['apple', 'banana', 'cherry']
first = fruits[0] # 'apple'
fruits.remove("banana") # ['apple', 'cherry']
size = len(fruits) # 2
The walkthrough starts with creating a list and moves on to adding, reading, changing and removing items, joining lists, and every list method with its return value. The last sections cover copying, sorting and the common traps, all checked on Python 3.14.
1. Creating a List
Square brackets create a list literal, and the list() constructor converts any iterable, such as a string, tuple or range().
empty = []
subjects = ["physics", "chemistry", "mathematics"]
ids = [0, 1, 2, 3, 4]
mixed = [0, "one", 2, "three"]
nested = [["A", "B", "C"], ["D", "E", "F"]]
from_range = list(range(5)) # [0, 1, 2, 3, 4]
from_string = list("abc") # ['a', 'b', 'c']
Use a comprehension, not multiplication, to create a list of lists. The expression [[0] * 3] * 3 repeats one inner list three times, so changing one row changes all of them.
wrong = [[0] * 3] * 3
wrong[0][0] = 1 # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
right = [[0] * 3 for _ in range(3)]
right[0][0] = 1 # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
2. Adding Items in List
The append(item) method adds one item at the end, insert(index, item) adds it at a position, and extend(iterable) adds all items of another iterable. If the index is greater than the list size, insert() adds the item at the end without an error.
chars = []
chars.append("a")
chars.append("b") # ['a', 'b']
chars.insert(3, "c") # ['a', 'b', 'c']
chars.insert(10, "d") # ['a', 'b', 'c', 'd'], no error
chars.insert(0, "z") # ['z', 'a', 'b', 'c', 'd']
chars.extend(["e", "f"]) # ['z', 'a', 'b', 'c', 'd', 'e', 'f']
Passing a list to append() adds the whole list as a single nested item, which is a frequent mistake when extend() was meant.
3. Accessing Items in List
Square brackets with an index return a single item, and slicing returns a new list.
- A negative index counts from the end, so -1 is the last item.
- list[m:n] returns the items from index m (included) to n (not included).
- If m is omitted, the slice starts at 0, and if n is omitted, it runs to the end.
- A third value sets the step, so [::2] takes every second item and [::-1] reverses the list.
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
first = nums[0] # 0
last = nums[-1] # 9
part = nums[1:5] # [1, 2, 3, 4]
head = nums[:3] # [0, 1, 2]
tail = nums[7:] # [7, 8, 9]
back = nums[-8:-5] # [2, 3, 4]
evens = nums[::2] # [0, 2, 4, 6, 8]
reversed_copy = nums[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
An index outside the list raises IndexError: list index out of range. Slices never raise this error and return an empty list instead.
4. Modifying a List
We assign a new value to an index to change one item. Assigning to a slice replaces a whole range, and the new range can have a different length.
chars = ["a", "b", "c"]
chars[2] = "d" # ['a', 'b', 'd']
chars[0:2] = ["x", "y", "z"] # ['x', 'y', 'z', 'd']
5. Iterating a List
A for loop visits the items in order, and enumerate() adds the index. The Python for loop tutorial covers zip() and other patterns.
chars = ["a", "b", "c"]
for i, ch in enumerate(chars):
print(i, ch)
0 a
1 b
2 c
Removing items from a list while looping over it skips elements. We loop over a copy, such as chars[:], or build a new list with a comprehension.
6. Check if a Given Item Exists in the List
The in operator returns True when the list contains the value. It scans the list from the start, so for many lookups in a large collection a set is faster.
chars = ["a", "b", "c"]
has_a = "a" in chars # True
has_d = "d" in chars # False
7. List Size
The len() function returns the number of items. An empty list is falsy, so if not items checks for emptiness.
chars = ["a", "b", "c"]
size = len(chars) # 3
is_empty = not [] # True
8. Removing Items from the List
Python offers four ways to remove items, and the right one depends on whether we know the value or the position.
- remove(value) deletes the first item with that value.
- pop() deletes and returns the last item, or the item at a given index.
- clear() deletes all items.
- del deletes an item or a slice by index, or the whole variable.
8.1. remove()
The method searches for the value and deletes its first occurrence. It returns None and raises ValueError when the value is missing.
chars = ["a", "b", "c", "b"]
chars.remove("b") # ['a', 'c', 'b']
>>> chars.remove("x")
ValueError: list.remove(x): x not in list
8.2. pop()
The method deletes the item at an index and returns it. Without an index, it pops the last item, which makes a list a fast stack.
chars = ["a", "b", "c", "d"]
last = chars.pop() # 'd', chars is ['a', 'b', 'c']
second = chars.pop(1) # 'b', chars is ['a', 'c']
Popping from an empty list raises IndexError: pop from empty list.
8.3. clear()
The method empties the list in place, and every name that refers to the list sees the change.
chars = ["a", "b", "c", "d"]
chars.clear() # []
8.4. del Keyword
The del statement removes an item or a slice by index. Used on the name, it deletes the variable, and reading it afterwards raises NameError.
chars = ["a", "b", "c", "d"]
del chars[0] # ['b', 'c', 'd']
del chars[:2] # ['d']
9. Joining Two Lists
The + operator returns a new list, while extend() and += change the first list in place. Unpacking with * joins any number of iterables.
chars = ["a", "b", "c"]
nums = [1, 2, 3]
joined = chars + nums # ['a', 'b', 'c', 1, 2, 3]
merged = [*chars, *nums, "z"] # ['a', 'b', 'c', 1, 2, 3, 'z']
chars.extend(nums) # chars is ['a', 'b', 'c', 1, 2, 3]
10. List Methods
Methods that change the list return None, except pop(), so items = items.sort() loses the list. Of the others, only copy(), count() and index() return a value.
| Method | Description | Return value |
|---|---|---|
| append(x) | Adds x at the end | None |
| extend(iterable) | Adds every item of the iterable | None |
| insert(i, x) | Adds x before index i | None |
| remove(x) | Deletes the first x | None, ValueError if missing |
| pop([i]) | Deletes the item at i (default last) | The removed item |
| clear() | Deletes all items | None |
| index(x[, start[, end]]) | Finds the first position of x | int, ValueError if missing |
| count(x) | Counts occurrences of x | int |
| sort(key=None, reverse=False) | Sorts in place | None |
| reverse() | Reverses in place | None |
| copy() | Shallow copy | A new list |
10.1. sort() and sorted()
The sort() method sorts the list in place, and the built-in sorted() returns a new sorted list and leaves the original alone. Both accept key and reverse.
chars = ["a", "c", "B", "d"]
result = chars.sort() # None, chars is ['B', 'a', 'c', 'd']
by_lower = sorted(chars, key=str.lower) # ['a', 'B', 'c', 'd']
newest_first = sorted([3, 1, 2], reverse=True) # [3, 2, 1]
Uppercase letters sort before lowercase ones, because sorting compares Unicode code points. The key=str.lower argument gives a case-insensitive order.
10.2. reverse()
The method reverses the items in place. The slice [::-1] and reversed() leave the original list unchanged.
chars = ["a", "b", "c", "d"]
chars.reverse() # ['d', 'c', 'b', 'a']
10.3. copy()
The method returns a shallow copy. The new list is a separate object, but nested lists inside it are shared with the original. The copy.deepcopy() function copies the nested objects too.
import copy
grid = [[1, 2], [3, 4]]
shallow = grid.copy()
deep = copy.deepcopy(grid)
grid[0][0] = 99
shallow_sees = shallow[0][0] # 99, the inner list is shared
deep_sees = deep[0][0] # 1
Plain assignment, as in b = a, copies nothing. Both names refer to the same list.
10.4. count() and index()
The count() method returns how often a value appears, and index() returns the position of its first occurrence. An optional start position finds later occurrences.
letters = ["a", "b", "a", "c", "a"]
times = letters.count("a") # 3
first_a = letters.index("a") # 0
next_a = letters.index("a", 1) # 2
>>> letters.index("z")
ValueError: list.index(x): x not in list
10.5. len(), min(), max() and sum()
These built-in functions are not list methods, but they are the most common way to summarize a list. The min() and max() functions raise ValueError on an empty list unless we pass default.
scores = [3, 9, 2]
count = len(scores) # 3
lowest = min(scores) # 2
highest = max(scores) # 9
total = sum(scores) # 14
safe = max([], default=0) # 0
longest = max(["tea", "sugar"], key=len) # 'sugar'
11. Real-World Example: An Undo Stack
Text editors keep the history of changes in a list used as a stack. The append() method pushes an action and pop() takes back the latest one, and both are fast at the end of a list.
history = []
text = ""
for word in ["Hello", " World", "!"]:
history.append(text) # save the state before the change
text += word
text = history.pop() # undo the last change
undone = text # 'Hello World'
remaining = len(history) # 2
For a queue that removes items from the front, list.pop(0) shifts every remaining item. The collections.deque class does that in constant time.
12. Python List FAQs
Lists get compared with tuples and arrays a lot, and removing repeated values trips up many beginners.
12.1. What is the difference between a list and a tuple?
A list is mutable and has methods to add and remove items, while a tuple cannot change after creation and can be a dictionary key. The full comparison is in Python lists vs tuples.
12.2. Is a Python list the same as an array?
A list is a dynamic array of references, so it can hold any mix of types. For large numeric data, the array module or NumPy arrays store raw numbers and use far less memory.
12.3. How do we remove all occurrences of a value?
The remove() method deletes only the first one, so we build a new list with a comprehension.
nums = [1, 2, 1, 3, 1]
without_ones = [n for n in nums if n != 1] # [2, 3]
13. Conclusion
Lists are the general-purpose container of Python, indexed and sliced from either end. Items go in with append(), insert() and extend(), come out with remove(), pop(), clear() and del, and we keep in mind that methods which change the list return None. Copies need copy() or deepcopy(), because assignment only adds another name.
14. References
Happy Learning !!