Python String Replace: Syntax and Usage
Learn how to use Python's `replace()` method, including the `count` parameter, case sensitivity, and when to use `re.sub` instead.
Python's str.replace(old, new[, count]) method returns a copy of a string with occurrences of old replaced by new. If count is omitted, all occurrences are replaced; if it is provided, only the first count occurrences are replaced. Because Python strings are immutable, the original string is not changed, and a new string object is returned.
Basic Usage and Return Value
The simplest call replaces all occurrences of a substring:
text = "one fish, two fish, red fish, blue fish" result = text.replace("fish", "bird") print(result) # one bird, two bird, red bird, blue bird
replace works with substrings of any length, not just single characters. It also accepts an empty string as the old argument, which inserts new at every position, including the start and end:
"abc".replace("", "-") # -a-b-c-
This behavior is rarely useful, but it can surprise developers who assume an empty pattern is ignored.
Limiting Replacements with the Count Parameter
The optional count parameter restricts how many replacements are performed from the left. This is useful when you want to replace only the first or first few occurrences:
s = "apple, apple, apple" s.replace("apple", "orange", 2) # orange, orange, apple
If count is omitted, negative, or larger than the actual number of occurrences, all occurrences are replaced. replace never matches overlapping substrings; it scans left to right and resumes after each match. For example, replacing "aa" with "b" in "aaa" yields "ba", not "bb", because the second match would need to overlap the first.
Case Sensitivity and Case-Insensitive Replacement
The replace method is case-sensitive. To make matching case-insensitive, you can normalize the string to one case before replacing, but that changes the output case. For pattern-based replacement that leaves non-matching text unchanged, re.sub with the re.IGNORECASE flag is more appropriate:
import re s = "Hello World, hello universe" re.sub("hello", "hi", s, flags=re.IGNORECASE) # hi World, hi universe
re.sub replaces all matches by default and also supports a count argument. For simple literal replacements, str.replace is faster and simpler; for case-insensitive or pattern matching, re.sub is the right tool.
Chaining Multiple Replacements
To replace several different substrings, you can chain replace calls. Each call returns a new string, so the order matters:
s = "cat and dog" s.replace("cat", "bird").replace("dog", "fish") # bird and fish
Be careful when replacement strings contain the original search string. For example, replacing "a" with "aa" in "a" gives "aa", but if you chain replacements, the second call may operate on already-replaced text. In such cases, consider a single traversal using re.sub with a callback or a dictionary mapping.
Performance and Memory Behavior
Each replace call creates a new string and copies the content, even if only one occurrence changes. This is inherent to Python's immutable string design. For a one-off replacement, the overhead is negligible. However, in a loop that repeatedly modifies a string, this can lead to quadratic time complexity because each iteration copies the growing string. Instead, collect parts in a list and join them, or use re.sub for complex transformations.
replace with a simple literal is implemented in C and is very fast. If you need to replace many different substrings, a single re.sub with a function that looks up replacements from a dictionary is often more efficient than chaining many replace calls, especially for long strings.
When to Use re.sub Instead of replace
str.replace is limited to literal substring replacement. If you need to match patterns, use re.sub. Common cases include:
- Replacing with case-insensitive matching
- Replacing based on regex patterns (e.g., digits, word boundaries)
- Using a function to compute the replacement dynamically
| Feature | str.replace | re.sub |
|---|---|---|
| Pattern matching | Literal only | Regex patterns |
| Case-insensitive | Not directly | With re.IGNORECASE |
| Count limit | Yes | Yes |
| Replacement function | No | Yes |
| Performance for simple literals | Faster | Slower |
For simple literal replacements, replace is the right choice. For anything that requires pattern logic, re.sub is more expressive and avoids multiple passes over the string.
Common Pitfalls and Edge Cases
One frequent mistake is assuming replace modifies the string in place. Because strings are immutable, forgetting to assign the result leads to no change:
s = "hello" s.replace("l", "L") # result discarded print(s) # still "hello"
Another edge case is overlapping substrings. replace does not handle overlaps; it scans left to right and skips the matched portion. For example, replacing "aba" in "ababa" yields "Xba" because the first match consumes positions 0 through 2, leaving "ba". This is usually the desired behavior, but it can surprise developers expecting all possible matches.
Finally, replacing old with an empty string effectively deletes the old substring. This is a common way to remove characters or substrings:
"remove spaces here".replace(" ", "") # removespaceshere
Be aware that replace with an empty old argument inserts the replacement everywhere, which is rarely what you need. Always test edge cases with short examples to confirm the behavior matches your expectation.