If you write this code:
name = "alice"
name.upper()
print(name)
you will get "alice" and NOT "ALICE".
This is the first fundamental truth about Python strings: they're immutable.
What Immutability Means
When you call upper(), Python doesn't reach into memory and change the letters. It can't. String objects, once created, cannot be modified.
What upper() actually does is build an entirely new string and return it:
>>> s = "hello"
>>> result = s.upper() # Creates new string "HELLO"
>>> print(s) # Original unchanged
hello
>>> print(result) # New string
HELLO
The original string still exists, unchanged. The variable s still points to it. upper() handed us a completely different object.
To "modify" a string, you have to reassign:
name = name.upper() # Now name points to the new string
Why This Design?
It's not arbitrary. Immutability enables:
1. Dictionary Keys
Strings can be used as dict keys because Python guarantees they won't change after hashing.
2. Thread Safety
Multiple threads can read the same string without locks—neither can modify it.
3. Memory Optimization
Python can reuse identical strings (interning):
>>> a = "hello"
>>> b = "hello"
>>> a is b # Same object!
True
The Performance Trap
Immutability has a cost. Consider:
result = ""
for line in log_file:
result += line
Each +=:
- Creates a new string
- Copies all existing characters
- Appends the new line
- Discards the old string
For 10,000 lines: 10,000 objects, ~50 million character copies.
Time complexity: O(n²)—doubling input quadruples runtime.
The Solution
Collect pieces in a list. Join at the end:
parts = []
for line in log_file:
parts.append(line)
result = "\n".join(parts)
join() knows the final size, allocates once, and copies each piece exactly once.
Time complexity: O(n)
For 10,000 lines:
- Concatenation: ~500ms
-
join(): ~1ms
500x faster.
The Pattern
This applies to:
- Log aggregation
- CSV/JSON generation
- Building LLM prompts
- Any assembly of text from parts
Whenever you reach for += in a loop, stop. Use a list and join().
Key Takeaways
- String methods don't modify—they return new strings
- Reassign to "change":
s = s.upper() - Immutability enables hashing, thread safety, memory optimization
- Never concatenate strings in loops—use
join()
𝘍𝘳𝘰𝘮 𝘮𝘺 𝘶𝘱𝘤𝘰𝘮𝘪𝘯𝘨 𝘣𝘰𝘰𝘬 "𝘡𝘦𝘳𝘰 𝘵𝘰 𝘈𝘐 𝘌𝘯𝘨𝘪𝘯𝘦𝘦𝘳: 𝘗𝘺𝘵𝘩𝘰𝘯 𝘍𝘰𝘶𝘯𝘥𝘢𝘵𝘪𝘰𝘯𝘴." 𝘚𝘶𝘣𝘴𝘤𝘳𝘪𝘣𝘦 𝘵𝘰 𝘮𝘺 𝘚𝘶𝘣𝘴𝘵𝘢𝘤𝘬 𝘧𝘰𝘳 𝘤𝘩𝘢𝘱𝘵𝘦𝘳𝘴 𝘭𝘪𝘬𝘦 𝘵𝘩𝘪𝘴 𝘪𝘯 𝘺𝘰𝘶𝘳 𝘪𝘯𝘣𝘰𝘹.
https://substack.com/@samuelochaba?utm_campaign=profile&utm_medium=profile-page
Top comments (0)