List Methods and Supported Operators in Python: Complete List Manipulation and Operation Reference Guide

List methods and supported operators in python

List methods and supported operators in python

Lists are one of the very first data structures I learned in Python, and honestly, they’re also one of the ones I use the most, even years later. What surprised me as I got deeper into Python was just how many built-in methods and operators lists support — far more than I initially realized. In this guide, I want to go through every commonly used list method and operator, explain how each works internally, and cover the performance characteristics that actually matter when I’m writing production code.

What Is a Python List?

A Python list is a mutable, ordered collection that can hold elements of mixed types. Internally, CPython implements a list as a dynamic array — a contiguous block of memory holding pointers to Python objects, with some extra capacity pre-allocated to make appending efficient.

my_list = [1, "two", 3.0, [4, 5], True]
print(my_list)

Output:

[1, 'two', 3.0, [4, 5], True]

Core List Methods

append()

Adds a single element to the end of the list.

fruits = ["apple", "banana"]
fruits.append("cherry")
print(fruits)

Output:

['apple', 'banana', 'cherry']

append() runs in amortized O(1) time. CPython over-allocates extra space when the underlying array needs to grow, so most appends don’t require a full reallocation — only occasionally does the array need to be resized and copied.

extend()

Adds all elements from an iterable to the end of the list.

fruits.extend(["mango", "kiwi"])
print(fruits)

Output:

['apple', 'banana', 'cherry', 'mango', 'kiwi']

A common mistake I see (and made myself early on) is using append() when extend() was intended:

fruits.append(["grape", "fig"])
print(fruits)

Output:

['apple', 'banana', 'cherry', 'mango', 'kiwi', ['grape', 'fig']]

Notice append() added the whole list as a single nested element, while extend() would have added each item individually.

insert()

Inserts an element at a specific index, shifting subsequent elements to the right.

fruits.insert(1, "orange")
print(fruits)

Output:

['apple', 'orange', 'banana', 'cherry', 'mango', 'kiwi', ['grape', 'fig']]

insert() is O(n) because every element after the insertion point has to shift over by one position in memory.

remove()

Removes the first matching value.

fruits.remove("banana")
print(fruits)

Output:

['apple', 'orange', 'cherry', 'mango', 'kiwi', ['grape', 'fig']]

If the value isn’t found, remove() raises a ValueError.

pop()

Removes and returns an element at a given index (default: the last element).

last_item = fruits.pop()
print(last_item)
print(fruits)

first_item = fruits.pop(0)
print(first_item)

Output:

['grape', 'fig']
['apple', 'orange', 'cherry', 'mango', 'kiwi']
apple

Popping from the end is O(1); popping from the beginning or middle is O(n) because remaining elements have to shift left.

index()

Returns the index of the first matching value.

print(fruits.index("mango"))

Output:

2

count()

Counts occurrences of a value.

numbers = [1, 2, 2, 3, 2, 4]
print(numbers.count(2))

Output:

3

sort()

Sorts the list in place.

numbers = [5, 2, 8, 1, 9]
numbers.sort()
print(numbers)

numbers.sort(reverse=True)
print(numbers)

Output:

[1, 2, 5, 8, 9]
[9, 8, 5, 2, 1]

sort() uses a highly optimized algorithm called Timsort, which is a hybrid of merge sort and insertion sort, tuned to perform very well on real-world, partially ordered data. Its worst-case time complexity is O(n log n).

I can also sort using a custom key:

words = ["banana", "kiwi", "fig", "apple"]
words.sort(key=len)
print(words)

Output:

['fig', 'kiwi', 'apple', 'banana']

reverse()

Reverses the list in place.

numbers.reverse()
print(numbers)

Output:

[1, 2, 5, 8, 9]

copy()

Creates a shallow copy of the list.

original = [1, 2, [3, 4]]
copied = original.copy()
copied[0] = 100
print(original)
print(copied)

Output:

[1, 2, [3, 4]]
[100, 2, [3, 4]]

I have to be careful here: copy() only performs a shallow copy, meaning nested mutable objects (like the inner list [3, 4]) are shared between both lists.

copied[2].append(5)
print(original)
print(copied)

Output:

[1, 2, [3, 4, 5]]
[100, 2, [3, 4, 5]]

For a true deep copy, I use the copy module:

import copy
deep_copied = copy.deepcopy(original)

clear()

Removes all elements from the list.

sample = [1, 2, 3]
sample.clear()
print(sample)

Output:

[]

Supported Operators

Concatenation with +

a = [1, 2, 3]
b = [4, 5, 6]
print(a + b)

Output:

[1, 2, 3, 4, 5, 6]

This creates a new list — it doesn’t modify a or b in place.

Repetition with *

print([0] * 5)

Output:

[0, 0, 0, 0, 0]

A subtle trap I’ve hit before: multiplying a list containing a mutable object doesn’t create independent copies of that object.

grid = [[0] * 3] * 3
grid[0][0] = 1
print(grid)

Output:

[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

All three rows are actually references to the same inner list, so modifying one modifies all of them. The correct way to build an independent grid is with a list comprehension:

grid = [[0] * 3 for _ in range(3)]
grid[0][0] = 1
print(grid)

Output:

[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

Membership Testing with in

print(3 in [1, 2, 3])
print(9 in [1, 2, 3])

Output:

True
False

Membership testing on a list is O(n) because Python has to check elements one by one. For frequent membership checks on large collections, I switch to a set, which offers average O(1) lookup.

Comparison Operators

Lists support element-wise comparison:

print([1, 2, 3] == [1, 2, 3])
print([1, 2, 3] < [1, 2, 4])
print([1, 2] < [1, 2, 3])

Output:

True
True
True

Comparisons work lexicographically, similar to how strings compare — Python compares elements pairwise until it finds a difference.

Slicing

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:5])
print(numbers[:3])
print(numbers[7:])
print(numbers[::2])
print(numbers[::-1])

Output:

[2, 3, 4]
[0, 1, 2]
[7, 8, 9]
[0, 2, 4, 6, 8]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Slicing always creates a new list and runs in O(k), where k is the length of the slice.

Internal Working: Why Lists Are Fast for Some Things, Slow for Others

Because Python’s list is a dynamic array under the hood, it gets:

This is the exact opposite trade-off profile of a linked list, which offers O(1) insertion/removal (given a node reference) but O(n) indexing.

Real-World and Automation Use Cases

def chunk_list(data, size):
    return [data[i:i + size] for i in range(0, len(data), size)]

print(chunk_list(list(range(10)), 3))

Output:

[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Common Mistakes and Debugging Tips

  1. Mutating a list while iterating over it. This can cause elements to be skipped or processed twice.
nums = [1, 2, 3, 4, 5]
for n in nums:
    if n % 2 == 0:
        nums.remove(n)
print(nums)

Output:

[1, 3, 5]

This happens to work for this specific case but is fragile in general — I always iterate over a copy (for n in nums[:]) or build a new list instead when I need to modify while iterating.

  1. Using * to create nested lists, which shares references as shown earlier.
  2. Confusing sort() with sorted(). sort() modifies the list in place and returns None; sorted() returns a new list and leaves the original untouched.
  3. Using remove() expecting it to remove all occurrences. It only removes the first match.

Best Practices I Follow

FAQs

Q: What’s the time complexity of list.append()? Amortized O(1).

Q: Is list.sort() stable? Yes, Timsort is a stable sorting algorithm — equal elements retain their relative order.

Q: How do I remove duplicates from a list? list(set(my_list)) if order doesn’t matter, or list(dict.fromkeys(my_list)) if I need to preserve order.

Q: What’s the difference between remove() and del? remove(value) deletes the first occurrence of a value; del list[index] deletes by position.

Troubleshooting Tips

Summary

Python’s list methods and operators cover an impressively wide range of operations, and understanding their time complexity and internal behavior has made a real difference in how I write efficient, bug-free code. The dynamic array design behind Python lists explains virtually every performance characteristic I’ve come across — fast indexing and appending, slower insertion and removal in the middle, and the sharing pitfalls that come with shallow copies.

References

Exit mobile version