What is the difference between re.match, re.search and re.findall in Python?
Short answer
match only checks the start of the string, search scans anywhere for the first match, and findall returns every match as a list. Mixing up match and search is the usual source of confusion.
The short version
re.match only looks at the start of the string, re.search scans the whole string for the first match anywhere, and re.findall returns every non-overlapping match as a list. The classic mistake is using match when you meant search, then wondering why a pattern that clearly appears in the text is not found.
The three in action
import re
re.match(r"\d+", "abc123") # None, no digits at the start
re.search(r"\d+", "abc123") # matches "123" anywhere
re.findall(r"\d+", "a1b22c333") # ['1', '22', '333'], every run of digits
match failed because the string does not begin with digits. search found the digits later in the string. findall returned all three digit groups.
match versus search
This is the distinction to internalise. match is anchored to the beginning, as if the pattern started with ^. search has no such anchor and finds the first match wherever it is. If you want "does this pattern appear anywhere", use search. Use match only when you specifically mean "does the string start with this".
match and search return a match object
Both return a match object (or None), not the text. Get the matched string with .group(), and check for a match with a truth test or by comparing to None.
m = re.search(r"\d+", "abc123")
if m:
print(m.group()) # "123"
Testing if m: works because a match object is truthy and None is falsy. Calling .group() on a None result raises AttributeError, which is a common error when you forget to check for no match.
findall returns strings, and groups change it
findall returns a list of strings directly, which is convenient. But if your pattern has capture groups, findall returns the groups instead of the whole match, which surprises people.
re.findall(r"(\w)(\d)", "a1b2") # [('a', '1'), ('b', '2')], tuples of groups
If you want the whole matches even while using groups, use finditer and call .group() on each, or make the groups non-capturing with (?:...).
finditer for many matches with detail
When you need each match's position or groups, finditer yields match objects rather than plain strings.
for m in re.finditer(r"\d+", "a1b22"):
print(m.group(), m.start())
This gives both the text and where it was found, which findall cannot.
Compile when reusing a pattern
If you use the same pattern many times, compile it once with re.compile and call methods on the result. It reads cleanly and avoids recompiling the pattern on every call, which matters in a loop over many strings.
digits = re.compile(r"\d+")
digits.search("abc123")