Look-behind is a zero-width assertion in Python’s re module that matches a position in the string only if it is immediately preceded by a specified pattern — without consuming or including the preceding text in the match. Positive look-behind (?<=pattern) succeeds only if the position is preceded by the pattern; negative look-behind (? succeeds only if it is not preceded by the pattern. Look-behind is ideal for context-dependent matching from the left: match something only if it comes after (or does not come after) a specific prefix, such as numbers after currency symbols, words after titles, or tags after opening delimiters. In 2026, look-behind remains a powerful regex feature — essential in data extraction, validation, cleaning (e.g., extract only after certain markers), log parsing, and vectorized pandas/Polars string operations where backward context matching scales efficiently across large datasets without extra text capture.
Here’s a complete, practical guide to look-behind in Python regex: positive and negative look-behind, syntax and examples, fixed-width requirements, real-world patterns, and modern best practices with raw strings, flags, compilation, and pandas/Polars integration.
Positive look-behind (?<=pattern) — matches the position only if preceded by pattern (doesn’t consume it).
import re
text = "The bar is preceded by foo, but not by baz."
# Match "bar" only if preceded by "foo "
pattern = r"(?<=foo )bar"
matches = re.findall(pattern, text)
print(matches) # ['bar']
# Match numbers preceded by "$" (currency)
prices = "Total $99.99 €200.00 £50"
print(re.findall(r'(?<=\\$)\\d+\\.\\d{2}', prices)) # ['99.99']
Negative look-behind (? — matches the position only if NOT preceded by pattern.
# Match numbers NOT preceded by "$"
print(re.findall(r'(?
Important note — look-behind in Python requires fixed-width patterns (since Python 3.7, variable-length is supported in some cases but can be slower or less reliable). Positive/negative look-behind must have a predictable length.
# Fixed-width look-behind — valid
print(re.findall(r'(?<=abc)def', "abcdef")) # ['def']
# Variable-width look-behind — may raise error or behave unexpectedly in older code
# Avoid patterns like (?<=a.*) — use alternatives or positive look-ahead instead
Real-world pattern: context-aware extraction in pandas — look-behind matches only after specific prefixes without capturing them.
import pandas as pd
df = pd.DataFrame({
'text': [
"Price: $99.99 (discounted)",
"Value: 50",
"Total: €200.00",
"Discount: $10"
]
})
# Extract amounts preceded by "$" (positive look-behind)
df['dollar_amount'] = df['text'].str.extract(r'(?<=\\$)\\d+\\.\\d{2}')
# Extract amounts NOT preceded by "$" or "€" (negative look-behind)
df['plain_number'] = df['text'].str.extract(r'(?
Best practices make look-behind safe, readable, and performant. Use positive look-behind for “preceded by” conditions — (?<=foo)bar — without including “foo” in the match. Use negative look-behind for exclusions — (? — matches only when prefix is absent. Stick to fixed-width look-behind patterns — avoid variable-length quantifiers inside look-behind for reliability. Modern tip: use Polars for large text columns — pl.col("text").str.extract(r'(?<=\\$)\\d+\\.\\d{2}') is 10–100× faster than pandas .str.extract(). Add type hints — str or pd.Series[str] — improves static analysis. Use raw strings r'pattern' — avoids double-escaping backslashes. Compile patterns with re.compile() for repeated use — faster and clearer. Combine with pandas.str — df['col'].str.contains(r'(?<=prefix)pattern', regex=True) for vectorized prefix-conditioned checks. Use look-behind to avoid capturing prefixes — keeps matches clean. Avoid overuse — look-behind can slow matching; test performance on large data.
Look-behind ((?<=...) positive, (? negative) checks backward conditions without consuming text — match only if preceded/not preceded by something. In 2026, use positive for required prefixes, negative for exclusions, raw strings, compile patterns, and vectorize in pandas/Polars. Master look-behind, and you’ll create context-aware, precise regex patterns for extraction, validation, and cleaning.
Next time you need to match only if preceded by something — use look-behind. It’s Python’s cleanest way to say: “Match this, but only if this came before.”