str.expandtabs()

Tabs become variable spaces — align to the next tab stop, not add a fixed count.

String methodPython 1.0+Live demo
Common call
text.expandtabs(4)
Returns
new str with tabs turned into aligning spaces
Replaces
a naive `text.replace("\t", " " * n)` — but that inserts a FIXED count, not aligning
Watch out
each tab expands by 1 to `tabsize` spaces depending on current column; newlines RESET the column
str.expandtabs(tabsizetabsizeThe tab stop width in columns. Every tab expands to enough spaces to reach the next multiple of tabsize. tabsize=0 removes tabs entirely.type: int · default: 8=8)
str

Demo

Live evaluation
Try:
Inputs
stringstrtext with \t tabs
tabsizeintcolumns per tab (empty=8)
Output
'a\tb'.expandtabs()
'a b'

Each tab expands to enough spaces to reach the next TAB STOP — a multiple of tabsize. That means the number of spaces inserted DEPENDS on the current column. In "ab\tc" with tabsize=4, the tab inserts 2 spaces (columns 2,3) to reach column 4. In "a\tc" with tabsize=4, the tab inserts 3 spaces. Newlines (\n) and carriage returns (\r) RESET the column counter to 0. tabsize=0 removes tabs entirely.

Parameters

NameTypeRequiredDescription
tabsizeintno (8)The tab stop width in columns. Every tab expands to enough spaces to reach the next multiple of tabsize. tabsize=0 removes tabs entirely.

Return value

strA copy of the string where every tab (U+0009) is replaced with spaces to align the following character at the next multiple of tabsize columns. Newlines and carriage returns reset the column counter to 0.

Common patterns

Convert tabs to 4-space indent
The most common use — align a chunk of tab-indented code.
code_with_spaces = code_with_tabs.expandtabs(4)
Align columns for display
Tabs between fields align them to consistent columns.
for name, age in people:
    print(f"{name}\t{age}".expandtabs(16))
Chain with splitlines for per-line control
expandtabs already handles newlines correctly, but chaining reads clearly.
for line in text.expandtabs(4).splitlines():
    ...
Remove tabs entirely
tabsize=0 is documented as removing tabs — a niche but real use.
no_tabs = text.expandtabs(0)

Examples

1. Default tabsize=8
"a\tb".expandtabs()
Returns
"a b" # 7 spaces to reach col 8
2. tabsize=4
"a\tb".expandtabs(4)
Returns
"a b" # 3 spaces to reach col 4
3. Already at tab stop
"abcd\te".expandtabs(4)
Returns
"abcd e" # 4 spaces to next stop
4. Partial fill
"ab\tc".expandtabs(4)
Returns
"ab c" # 2 spaces
5. Newline resets
"aa\tb\nc\td".expandtabs(4)
Returns
"aa b\nc d" # col resets after \n
6. No tabs, unchanged
"hello".expandtabs(4)
Returns
"hello"
7. tabsize=0 removes
"a\tb\tc".expandtabs(0)
Returns
"abc"

Pitfalls

1. NOT the same as replace("\t", " " * n)
The classic mistake. Naive replace inserts a fixed count of spaces — losing the alignment property. expandtabs aligns to the next multiple of tabsize.
Fixed-count replace
"ab\tcd".replace("\t", "  ")
"ab cd" # column 4, wrong for tabsize=4
expandtabs aligns
"ab\tcd".expandtabs(4)
"ab cd" # column 4, correctly aligned
2. Newline behavior — column RESETS at \n and \r
The column counter is reset by newline and carriage return. Tabs after a newline align relative to the start of the new line, not the original column. Miss this and tables misalign across lines.
Assumed continuous
# expected tabs to continue counting from before \n
they do not
Newlines reset
"aaaa\tb\nc\td".expandtabs(4)
"aaaa b\nc d" # both align to column 4
3. tabsize=0 REMOVES tabs, does not raise
A zero tabsize might look like a mistake, but Python documents it: tabs are dropped entirely. Handy for stripping tabs, but confusing if you expected a division-by-zero style error.
Assumed error
"a\tb".expandtabs(0)
"ab" # tab removed
By design
# tabsize=0 is documented as tab-removal
4. Wide characters count as ONE column
expandtabs counts characters, not visual width. CJK characters, emoji, and other wide-glyph characters count as one column each. Alignment based on visual width requires wcwidth or a similar library.
Off by visual width
"漢\tb".expandtabs(4)
"漢 b" # aligned by char count, misaligned visually
Use a width library
import wcwidth
# manual alignment based on wcwidth.wcswidth()
visually aligned

When to use

Use it
  • Converting tab-indented code to spaces (a common pre-commit step)
  • Aligning columns of a data table for display
  • Normalizing text before layout or width calculations
  • Preparing content for a rendering context that does not support tabs
Reach for something else
  • "Insert N spaces where a tab was" → str.replace is simpler
  • Visual-width alignment with wide characters → use wcwidth
  • Tab-to-tab spacing (not fill) → do it manually
  • Regex-based tab handling → probably overkill

Notes

Complexity
O(n) — one linear scan
Return
A new string; the original is unchanged (strings are immutable)
CPython impl
Objects/unicodeobject.c :: unicode_expandtabs
Memory
Allocates one new string
Thread-safe
Yes — strings are immutable

FAQ

replace inserts a FIXED number of characters everywhere a tab appears. expandtabs inserts a VARIABLE count that aligns the next character to the next tab stop. Only expandtabs preserves the "tab stop" alignment property.

History

1.0
expandtabs() has been part of str since Python 1.0.
3.0
Full Unicode support; behavior otherwise unchanged.