The Problem with Unnecessary Captures
Every set of parentheses (...) in a regex creates a capture group. The engine allocates memory to store the matched text for each group, and your code must index into the results by position. If you are using parentheses purely for grouping — to apply a quantifier or alternation — but do not need the captured text, you are paying a memory and readability cost for nothing.
Non-Capturing Group Syntax: (?:...)
Capturing: (https?|ftp)://
Non-capturing: (?:https?|ftp)://
Both patterns match http:// or https:// or ftp://. The difference: the capturing version stores the matched protocol in group(1). The non-capturing version does not store it, and does not shift the numbering of subsequent groups.
Why group numbering matters:
# With capturing group for alternation:
pattern = r'(https?|ftp)://([\w.]+)'
m = re.search(pattern, 'https://example.com')
print(m.group(1)) # 'https' — the protocol we don't need
print(m.group(2)) # 'example.com' — what we actually want
# With non-capturing group:
pattern = r'(?:https?|ftp)://([\w.]+)'
m = re.search(pattern, 'https://example.com')
print(m.group(1)) # 'example.com' — group 1 is now what we want
When to Use Each Group Type
| Situation | Use |
|---|---|
| You need the matched text | (...) capturing group |
| You need the matched text and want readable code | (?P<name>...) named group |
| You are grouping for alternation or quantifiers only | (?:...) non-capturing group |
| You need to prevent backtracking | (?>...) atomic group |
| You need to check context without consuming | (?=...) / (?<=...) lookaround |