csv.DictReader

The first row becomes the keys, every later row a dict. Blank lines are skipped, a short row is padded with restval, and a long row puts its extra values in a list under the key None.

csv classPython 2.3+ (plain dict rows since 3.8)Live demo
Common call
for row in csv.DictReader(f): row['name']
Returns
{'name': 'Ada', 'age': '36'} per row
Replaces
dict(zip(header, row)) for every row by hand
Watch out
values are still strings; extra fields land under the key None
csv.DictReader(ff — A file opened with newline='' (or any iterable of lines).type: iterable of str · required, fieldnamesfieldnames — The keys. None reads them from the first row; given, the first row is treated as data.type: sequence of str · default: None=None, restkeyrestkey — Key for the list of values beyond the last field name.type: hashable · default: None=None, restvalrestval — Value for field names that a short row has no value for.type: any · default: None=None, dialectdialect — Passed to csv.reader together with any other keyword arguments (delimiter=…).type: str | Dialect · default: 'excel'='excel', *args, **kwds)
→ DictReader

Demo

Live evaluation
CSV text with a header line (\n = line break) → a list of dicts.
Try:
Inputs
textstrCSV text, \n = line break
Code
import csv, io
text = 'name,age\\nAda,36\\nBob,41'.replace('\\n', '\n')
list(csv.DictReader(io.StringIO(text, newline='')))
Result
[{'name': 'Ada', 'age': '36'}, {'name': 'Bob', 'age': '41'}]

A repeated column name keeps the LAST value (dict(zip(...)) overwrites) but the first position. A header with no data rows gives []. With fieldnames=[] every value counts as "extra": the dict holds a single key, None. In restkey / restval the long row 1,2,3,4 keeps ["3", "4"] under None and the short row 5 gets None for b — the dict itself never complains, which is how wrong delimiters go unnoticed.

Parameters

NameTypeRequiredDescription
fiterable of stryesA file opened with newline='' (or any iterable of lines).
fieldnamessequence of strno (None)The keys. None reads them from the first row; given, the first row is treated as data.
restkeyhashableno (None)Key for the list of values beyond the last field name.
restvalanyno (None)Value for field names that a short row has no value for.
dialectstr | Dialectno ('excel')Passed to csv.reader together with any other keyword arguments (delimiter=…).

Return value

DictReader — An iterator of dicts, one per non-blank row. .fieldnames holds the keys; .line_num and .reader are also available.

Common patterns

Read a CSV file into a list of dicts
utf-8-sig drops the BOM Excel puts in front of the first column name.
import csv
with open('people.csv', newline='', encoding='utf-8-sig') as f:
    people = list(csv.DictReader(f))
Convert columns while reading
Values are str; convert the ones you need.
import csv
with open('people.csv', newline='', encoding='utf-8') as f:
    people = [{**row, 'age': int(row['age'])} for row in csv.DictReader(f)]
Check the header first
Reading .fieldnames consumes the header row only.
import csv
with open('people.csv', newline='', encoding='utf-8') as f:
    rows = csv.DictReader(f)
    missing = {'name', 'age'} - set(rows.fieldnames or [])
    if missing:
        raise ValueError(f'missing columns: {missing}')

Examples

1. One dict per row
import csv, io f = io.StringIO('name,age\nAda,36\nBob,41\n', newline='') list(csv.DictReader(f))
Returns
[{'name': 'Ada', 'age': '36'}, {'name': 'Bob', 'age': '41'}]
2. fieldnames reads the header
import csv, io r = csv.DictReader(io.StringIO('name,age\nAda,36\n', newline='')) (r.fieldnames, r.line_num)
Returns
(['name', 'age'], 1)
3. Extra values under None
import csv next(csv.DictReader(['a,b', '1,2,3']))
Returns
{'a': '1', 'b': '2', None: ['3']}
4. Missing values get restval
import csv next(csv.DictReader(['a,b,c', '1'], restval=''))
Returns
{'a': '1', 'b': '', 'c': ''}
5. Other delimiters pass through
import csv next(csv.DictReader(['name;age', 'Ada;36'], delimiter=';'))
Returns
{'name': 'Ada', 'age': '36'}
6. A BOM sticks to the first key
import csv with open('b.csv', 'w', encoding='utf-8-sig', newline='') as f: f.write('name,age\r\nAda,36\r\n') with open('b.csv', encoding='utf-8', newline='') as f: keys = csv.DictReader(f).fieldnames keys
Returns
['\ufeffname', 'age']
7. Rows are plain dicts
import csv type(next(csv.DictReader(['a', '1']))).__name__
Returns
'dict'

Pitfalls

1. Wrong delimiter: one giant column
A semicolon file read with the default comma gives one key that contains the whole header — no error, just wrong data.
default delimiter
import csv
next(csv.DictReader(['name;age', 'Ada;36']))
{'name;age': 'Ada;36'}
delimiter=';'
import csv
next(csv.DictReader(['name;age', 'Ada;36'], delimiter=';'))
{'name': 'Ada', 'age': '36'}
2. KeyError from an Excel BOM
Files saved by Excel as "CSV UTF-8" start with a BOM. Read as utf-8 it becomes part of the first column name.
encoding='utf-8'
import csv
with open('b.csv', 'w', encoding='utf-8-sig', newline='') as f:
    f.write('name,age\r\nAda,36\r\n')
with open('b.csv', encoding='utf-8', newline='') as f:
    name = next(csv.DictReader(f))['name']
KeyError: 'name'
encoding='utf-8-sig'
import csv
with open('b.csv', 'w', encoding='utf-8-sig', newline='') as f:
    f.write('name,age\r\nAda,36\r\n')
with open('b.csv', encoding='utf-8-sig', newline='') as f:
    name = next(csv.DictReader(f))['name']
name
'Ada'
3. Spaces after the commas end up in the keys
"name, age" has the key " age" (with a space). skipinitialspace=True drops spaces after each delimiter.
default
import csv
list(next(csv.DictReader(['name, age', 'Ada, 36'])))
['name', ' age']
skipinitialspace=True
import csv
list(next(csv.DictReader(['name, age', 'Ada, 36'], skipinitialspace=True)))
['name', 'age']

When to use

Use it
  • Files with a header row where you want columns by name
  • Code that should survive reordered columns
Reach for something else
  • Files without a header and fixed positions → csv.reader is simpler
  • Typed data and analysis → pandas.read_csv

Notes

CPython impl
Lib/csv.py — class DictReader wraps csv.reader; __next__ skips rows == [], builds dict(zip(fieldnames, row)) and adds restkey / restval
Row type
A plain dict since Python 3.8 (OrderedDict in 3.6–3.7)
fieldnames
A property: the first access reads the header row from the file; set it to rename columns before iterating

FAQ

with open(path, newline='', encoding='utf-8') as f: rows = list(csv.DictReader(f)). Each dict maps the header names to that row's values (all strings).