string.capwords

One line of Python: (sep or ' ').join(map(str.capitalize, s.split(sep))). That explains everything it does — including lowercasing the rest of each word and squeezing whitespace.

string functionPython 3 (all)Live demo
Common call
string.capwords("they're bill's friends")
Returns
"They're Bill's Friends"
Replaces
str.title(), which capitalizes after apostrophes and digits
Watch out
the rest of every word is lowercased: iPhone → Iphone
string.capwords(ss — The text.type: str · required, sepsep — None: split on runs of any whitespace, drop leading/trailing whitespace, join with ' '. A string: split on exactly that separator and join with it again. '' raises ValueError.type: str | None · default: None=None)
→ str

Demo

Live evaluation
The same text through capwords and str.title().
Try:
Inputs
textstrsome words
Code
import string
(string.capwords("they're bill's friends"), "they're bill's friends".title())
Result
("They're Bill's Friends", "They'Re Bill'S Friends")

title() treats every non-letter as a word boundary, so it gives "They'Re" and "1St"; capwords only splits on whitespace. Both lowercase the rest of each word. With sep=' ' the empty strings between doubled spaces are kept, so the spacing survives; with sep=None it collapses. In 'ΟΔΟΣ' the final capital sigma lowercases to ς, as in str.lower().

Parameters

NameTypeRequiredDescription
sstryesThe text.
sepstr | Noneno (None)None: split on runs of any whitespace, drop leading/trailing whitespace, join with ' '. A string: split on exactly that separator and join with it again. '' raises ValueError.

Return value

str — The capitalized words joined by sep (by a single space when sep is None).

Common patterns

Names and headings
Good default for user-entered names; check exceptions (McDonald, van Gogh) separately.
import string
display = string.capwords(raw_name.strip())
Capitalize hyphenated parts too
Run it per separator.
import string
name = "-".join(string.capwords(part) for part in raw.split("-"))
Capitalize only the first letter, keep the rest
capwords lowercases the rest; slice instead if you must keep inner capitals.
fixed = " ".join(w[:1].upper() + w[1:] for w in text.split())

Examples

1. Apostrophes are not word breaks
import string string.capwords("they're bill's friends")
Returns
"They're Bill's Friends"
2. Whitespace is normalized
import string string.capwords(' hello world\n')
Returns
'Hello World'
3. Rest of the word lowercased
import string string.capwords('HELLO wORLD')
Returns
'Hello World'
4. sep splits and joins
import string string.capwords('a-b c-d', '-')
Returns
'A-B c-D'
5. sep=' ' keeps the spacing
import string string.capwords(' a b ', ' ')
Returns
' A B '
6. Digits do not start a word
import string string.capwords('1st place')
Returns
'1st Place'
7. Empty separator
import string string.capwords('abc', '')
Returns
ValueError: empty separator

Pitfalls

1. Losing inner capitals
str.capitalize lowercases everything after the first character, so brand names and acronyms are flattened.
capwords
import string
string.capwords('NASA and iPhone')
'Nasa And Iphone'
first letter only
' '.join(w[:1].upper() + w[1:] for w in 'NASA and iPhone'.split())
'NASA And IPhone'
2. Expecting sep to split on more than one character type
sep is one exact string. Words separated by spaces are not split when sep is "-".
sep='-'
import string
string.capwords('jean-luc picard', '-')
'Jean-Luc picard'
both separators
import string
' '.join(string.capwords(w, '-') for w in 'jean-luc picard'.split())
'Jean-Luc Picard'

When to use

Use it
  • Title-casing names, headings and labels where title() would produce "Don'T"
  • Cleaning up messy spacing and capitalization in one call
Reach for something else
  • Text with acronyms or brand names that must keep their capitals
  • Real headline style (small words lowercase) → write your own rule

Notes

CPython impl
Lib/string.py: return (sep or ' ').join(map(str.capitalize, s.split(sep)))
Unicode
str.capitalize titlecases the first character (dž → Dž, fi → Fi) and lowercases the rest, with the Greek final-sigma rule
sep falsy
sep='' reaches s.split('') and raises ValueError: empty separator

FAQ

string.capwords(s) — or s.title() if the text has no apostrophes or digits. capwords("don't stop") gives "Don't Stop"; "don't stop".title() gives "Don'T Stop".