str.casefold()
The correct tool for case-insensitive string comparison — where lower() falls short on non-Latin scripts.
Common call
a.casefold() == b.casefold()
Returns
a new str — the original is unchanged
Replaces
a.lower() == b.lower() when Unicode correctness matters
Watch out
result may be LONGER than input (ß → ss); does not normalize accents
str.casefold()
→ str
Demo
Live evaluation
Try:
Inputs
stringstrthe source
Output
'HELLO'.casefold()
'hello'
casefold applies the Unicode "case-folding" mapping — an aggressive lowercase designed for comparison, not display. The German ß becomes "ss" (not ß) because case-insensitive Straße should match STRASSE. lower() would leave ß unchanged, breaking the match. For any user-input comparison across languages, casefold is the correct default.
Common patterns
Case-insensitive equality
The idiomatic way — works across languages.
if a.casefold() == b.casefold(): ...
Case-insensitive set membership
Fold both the item and the set contents.
allowed = {"gold", "silver", "bronze"} if user_input.casefold() in {x.casefold() for x in allowed}: ...
Case-insensitive sort key
casefold makes an excellent key for locale-neutral sorting.
names.sort(key=str.casefold)
Examples
1. Basic
"HELLO".casefold()
Returns
"hello"2. German ß expands
"straße".casefold()
Returns
"strasse"3. Straße == STRASSE
"Straße".casefold() == "STRASSE".casefold()
Returns
True4. lower() misses it
"Straße".lower() == "STRASSE".lower()
Returns
False # ß != ss5. Accents preserved
"CAFÉ".casefold()
Returns
"café"6. Empty is empty
"".casefold()
Returns
""Pitfalls
1. casefold is NOT the same as lower()
lower() is a simple case mapping — one character in, one character out. casefold applies the Unicode case-folding table, which can EXPAND a character (ß → ss) or map to unusual lowercase forms.
lower() misses ß
"Straße".lower() == "STRASSE".lower()
False # ß stays ß
casefold gets it
"Straße".casefold() == "STRASSE".casefold()
True
2. Length can change
ß casefolds to two characters. A length check before and after may fail unexpectedly.
Grew by one
len("Straße".casefold())
7 # was 6
Do not assume length preservation
# treat as arbitrary string transformation3. Does NOT normalize accents or diacritics
casefold folds case, not diacritics. "café" and "cafe" still compare unequal. For accent-insensitive matching, use unicodedata.normalize.
Still different
"café".casefold() == "cafe".casefold()
False
Normalize NFKD + strip combining
import unicodedata def strip_accents(s): return "".join(c for c in unicodedata.normalize("NFKD", s) if not unicodedata.combining(c))
accent-insensitive after this
4. Original string is NOT modified
Like all string methods, casefold returns a new string. Assigning it back is required for the folded value to persist.
Lost result
s = "HELLO" s.casefold() print(s)
"HELLO" # unchanged
Capture it
s = s.casefold() print(s)
"hello"
When to use
Use it
- Case-insensitive equality across any language
- Sort keys where case should not matter
- Building case-insensitive sets or lookups
- Any user-input comparison where locale-neutral behavior is required
Reach for something else
- Display formatting — lower() and upper() are for display, casefold is for comparison
- ASCII-only text where lower() is enough and reads more idiomatically
- Accent-insensitive matching → normalize with unicodedata first
- Length-sensitive operations after folding
Notes
Complexity
O(n)
Return
A new string; length may exceed the original due to expansions like ß → ss
CPython impl
Objects/unicodeobject.c :: unicode_casefold — applies Unicode's CaseFolding table
Memory
Allocates one new string
Thread-safe
Yes — strings are immutable
FAQ
lower() applies Unicode's simple lowercase mapping — one code point in, one out. casefold applies the Unicode case-folding mapping, which is designed for caseless comparison and handles special cases like ß → ss and Greek final sigma. Use lower for display; use casefold for equality.
History
3.3
casefold() introduced — replacing hand-rolled Unicode-aware lowercase workarounds.