DictWriter.writeheader
One call, one row: the fieldnames, formatted with the same dialect as the data. Call it once, before the first writerow.
Common call
w.writeheader()
Returns
the write() result, e.g. 10 for 'name,age\r\n'
Replaces
w.writer.writerow(fieldnames)
Watch out
not automatic — and calling it twice writes the header twice
DictWriter.writeheader()
→ int | None
Demo
Live evaluation
Comma-separated field names → the header line and what writeheader returned.
Try:
Inputs
fieldnameslist[str]column names
Code
import csv, io buf = io.StringIO(newline='') n = csv.DictWriter(buf, fieldnames=['name', 'age']).writeheader() (n, buf.getvalue())
Result
(10, 'name,age\r\n')
With fieldnames=[] the header is a row with no fields, which is written as a bare "\r\n" (2 characters). writeheader builds dict(zip(fieldnames, fieldnames)) and passes it to writerow, so it follows every dialect rule — under QUOTE_NONE a name containing a comma has nothing to protect it and csv.Error is raised.
Common patterns
Header, then rows
The usual three lines.
import csv with open('out.csv', 'w', newline='', encoding='utf-8') as f: w = csv.DictWriter(f, fieldnames=['name', 'age']) w.writeheader() w.writerows(rows)
Append without repeating the header
Only write it when the file is new or empty.
import csv, os new = not os.path.exists('log.csv') or os.path.getsize('log.csv') == 0 with open('log.csv', 'a', newline='', encoding='utf-8') as f: w = csv.DictWriter(f, fieldnames=['time', 'event']) if new: w.writeheader() w.writerow(entry)
Human-friendly header labels
writeheader always writes the keys; for other labels write the row yourself.
w = csv.DictWriter(f, fieldnames=['name', 'age']) w.writer.writerow(['Full name', 'Age (years)']) w.writerows(rows)
Examples
1. Writes the field names
import csv, io
buf = io.StringIO(newline='')
csv.DictWriter(buf, ['name', 'age']).writeheader()
buf.getvalue()
Returns
'name,age\r\n'2. Returns the write() result
import csv, io
csv.DictWriter(io.StringIO(), ['name', 'age']).writeheader()
Returns
103. Uses the dialect
import csv, io
buf = io.StringIO(newline='')
csv.DictWriter(buf, ['a', 'b'], dialect='excel-tab').writeheader()
buf.getvalue()
Returns
'a\tb\r\n'4. Same as writing the keys as a row
import csv, io
a, b = io.StringIO(newline=''), io.StringIO(newline='')
csv.DictWriter(a, ['x', 'y']).writeheader()
csv.writer(b).writerow(['x', 'y'])
a.getvalue() == b.getvalue()
Returns
True5. Read it back as fieldnames
import csv, io
buf = io.StringIO(newline='')
w = csv.DictWriter(buf, ['name', 'age'])
w.writeheader()
w.writerow({'name': 'Ada', 'age': 36})
buf.seek(0)
csv.DictReader(buf).fieldnames
Returns
['name', 'age']Pitfalls
1. Calling writeheader inside the loop
Every call writes another header row.
per row
import csv, io buf = io.StringIO(newline='') w = csv.DictWriter(buf, ['n']) for n in (1, 2): w.writeheader() w.writerow({'n': n}) buf.getvalue()
'n\r\n1\r\nn\r\n2\r\n'
once
import csv, io buf = io.StringIO(newline='') w = csv.DictWriter(buf, ['n']) w.writeheader() for n in (1, 2): w.writerow({'n': n}) buf.getvalue()
'n\r\n1\r\n2\r\n'
2. A generator as fieldnames
DictWriter turns an iterator into a list, so this works — but the generator is used up by the time you try to reuse it elsewhere.
reuse the generator
import csv, io names = (c for c in ['a', 'b']) csv.DictWriter(io.StringIO(), names).writeheader() list(names)
[]
use a list
import csv, io names = ['a', 'b'] csv.DictWriter(io.StringIO(), names).writeheader() list(names)
['a', 'b']
When to use
Use it
- Once, right after creating a DictWriter for a new file
Reach for something else
- Appending to a file that already has a header
- Custom header labels → w.writer.writerow([...])
Notes
CPython impl
Lib/csv.py — header = dict(zip(self.fieldnames, self.fieldnames)); return self.writerow(header)
Return value
Returns the writerow result since Python 3.8; None before
FAQ
Call writer.writeheader() once after creating the DictWriter and before writing rows. It writes the fieldnames as the first row.