What is the difference between re.match, re.search and re.fullmatch?
answer
- Three functions, one difference: where
- One is anchored, one scans
- Starts-with is not equals
- Validation wants the whole string
- All three return Match or None
basics
~20 sre.match only tries the pattern at the start of the string, re.search scans forward and returns the first match anywhere, and re.fullmatch requires the pattern to consume the whole string. All three return a Match object or None.
solid answer
~40 sAll three take a pattern and a string and return either a `re.Match` object or `None`; they differ only in where the match may begin and end. `re.match` anchors the attempt at position 0 — it can still stop short, so it means *starts with*, not *equals*. `re.search` walks left to right and returns the leftmost match wherever it occurs. `re.fullmatch` succeeds only if the pattern consumes the entire string, which is what you want for validation. The classic bug is reaching for `re.match` when you meant `re.search` (the name reads like a general 'does this match?') or when you meant `re.fullmatch`, so `re.match(r'\d+', '123abc')` happily succeeds on input you meant to reject. Always compare the result with `is not None` rather than relying on a bare truth test out of habit.
code
pycon · 9 lines>>> import re
>>> re.match(r"\d+", "abc 123")
>>> re.search(r"\d+", "abc 123")
<re.Match object; span=(4, 7), match='123'>
>>> re.match(r"\d+", "123abc")
<re.Match object; span=(0, 3), match='123'>
>>> re.fullmatch(r"\d+", "123abc")
>>> re.fullmatch(r"\d+", "123")
<re.Match object; span=(0, 3), match='123'>go deeper
Memorise the one-line distinction and be ready to say it without hedging: anchored at the start, scanning anywhere, whole string. Also recall that a miss returns None, not an empty string or False.
Explain the mechanics: match behaves like a leading start-of-string anchor but does not require the pattern to reach the end, which is why starts-with keeps being mistaken for equals. Show the Match/None contract and the span offsets.
Demonstrate the review instinct — treat re.match in a validation path as a defect, prefer re.fullmatch for whole-value checks, and point out where a plain str method is clearer and cheaper than invoking the regex engine at all.
Own the consistency angle: pick one convention for input validation across a codebase, make it lint-visible, and know when a regex is the wrong abstraction entirely versus a real parser for structured input.
## The three entry points Python's `re` module exposes three functions that answer *slightly* different questions about the same pattern and string. All of them return a `re.Match` object on success and `None` on failure, and all of them are also available as methods on a compiled pattern object (`re.Pattern.match`, `re.Pattern.search`, `re.Pattern.fullmatch`). **`re.match(pattern, string)`** attempts the match **only at position 0**. It behaves as if the pattern were prefixed with a start-of-string anchor. Crucially it does *not* require the pattern to reach the end of the string: a successful `re.match` means the string **starts with** something the pattern describes. **`re.search(pattern, string)`** scans the string from left to right and returns the **leftmost** position where the pattern can match. It is the general "does this appear anywhere?" function, and it is what most people actually mean when they say "match". **`re.fullmatch(pattern, string)`** succeeds only if the pattern matches **the whole string**, start to end. It is the validation function: use it whenever you are asking "is this entire input a well-formed X?". ```python import re re.match(r"\d+", "123abc") # Match - the string starts with digits re.match(r"\d+", "abc123") # None - position 0 is not a digit re.search(r"\d+", "abc123") # Match - found at offset 3 re.fullmatch(r"\d+", "123abc") # None - trailing 'abc' is not consumed re.fullmatch(r"\d+", "123") # Match ``` ## Why the naming trips people up Two independent traps sit on the word *match*. The first is cross-language muscle memory. In several other languages a `match` method means "find occurrences anywhere", which is Python's `search`. In Python, `re.match` is the *anchored* one. Anyone arriving from another ecosystem writes `re.match(r"error", line)` expecting a scan, gets `None` for every line where the word is not first, and concludes the regex is wrong. The second is the gap between *starts with* and *equals*. `re.match` is often used for validation — "does this identifier look right?" — and it silently accepts anything with a valid prefix. A resolution string checked with `re.match(r"\d+p", value)` accepts `"720p-corrupt"`. `re.fullmatch` is the correct tool and has existed since Python 3.4; before it, people appended an end anchor by hand, which is still fine but easier to forget. ## The return value Success gives a `re.Match` object. Its most useful methods for this purpose are `Match.group()` — with no argument, the entire matched text — and the offsets: `Match.start()`, `Match.end()` and `Match.span()`, which returns the `(start, end)` pair. Failure gives `None`, so the standard shape is: ```python m = re.search(r"\d+p", "clip_1080p.mp4") if m is not None: print(m.group(), m.span()) # 1080p (5, 10) ``` Two details matter here. First, `Match` objects are **always truthy**, including a zero-length match such as `re.search(r"\d*", "abc")`, which succeeds with an empty span at offset 0. So a bare `if m:` is not *wrong*, but `if m is not None:` states the intent and survives the reader who half-remembers "empty means false". Second, you cannot chain off the call — `re.search(...).group()` raises `AttributeError: 'NoneType' object has no attribute 'group'` the moment the pattern misses, which is one of the most common tracebacks in regex-heavy code. Bind the result first, then test. ## Anchored search on a compiled pattern The method forms accept optional `pos` and `endpos` arguments — `pattern.match(string, pos)` starts the anchored attempt at that offset instead of 0. This is how you walk a string token by token without slicing it (slicing copies; `pos` does not). Note that `pos` shifts *where the attempt begins*, not what a start-of-string anchor inside the pattern means; the anchor still refers to the real beginning of the string. ## Choosing between them Ask what the question actually is: - "Is this whole value well-formed?" → `re.fullmatch`. - "Does this line contain the thing?" → `re.search`. - "Does this string begin with the thing?" → `re.match`, and consider whether `str.startswith` would be clearer and faster — for a fixed literal prefix it is both, with no pattern to escape and no engine to invoke. A useful review habit: any `re.match` in a validation path is a defect until proven otherwise. Nine times in ten it should have been `re.fullmatch`, and the tenth time it deserves a comment saying the prefix is genuinely all you care about.
- If re.match succeeds, does that mean the whole string matched the pattern?No. `re.match` only anchors the attempt at position 0; the pattern may stop anywhere and leave the rest of the string unexamined. `re.match(r'\d+', '123abc')` returns a Match spanning (0, 3). If you need the entire string consumed, use `re.fullmatch`, or add an end-of-string anchor to the pattern.
- How would you rewrite re.search(r'x', s).group() so it does not blow up on a miss?Bind the result and test it: `m = re.search(r'x', s)` then `if m is not None: ...`. Chaining off the call raises `AttributeError: 'NoneType' object has no attribute 'group'` whenever the pattern misses, which turns an expected non-match into a crash. A conditional expression such as `m.group() if m else default` is fine too.
- When would you skip re entirely and use a str method?Whenever the test is a fixed literal. `str.startswith` replaces an anchored literal pattern, `in` replaces a literal `re.search`, and `str.endswith` replaces a trailing anchor. They are faster, need no escaping of characters such as `.` or `+`, and read plainly. Reach for `re` when the shape is genuinely variable.
Reading a printed form: match checks only the first box, search runs your eye down the page for the first hit, and fullmatch insists the entire page is filled in exactly as specified.
saying these in an interview costs you the question
- Believing re.match scans the whole string for a match
- Using re.match to validate that an entire value is well-formed
- Chaining .group() straight off re.search without a None check
- Thinking a failed match returns an empty string rather than None
- Assuming re.fullmatch is just re.match with a flag
- Reaching for a regex where str.startswith would do