PythonMastery
reference 5 min read · lesson 12 of 25 in Reference

Python File I/O Modes Cheat Sheet

1 · The lesson

read

This page collects the everyday ways to read and write files in Python: open() and its modes, the pathlib module for file names and folders, and the csv and json modules. Every example runs in your browser, so each file is created before it is read. For the full tutorial, see File I/O and File I/O basics.

Two rules first. Always open text files with encoding="utf-8" (an encoding is the rule that turns letters into bytes). And use with open(...): it closes the file even if your code fails.

1. Read and write in one line

A Path object is the name of a file or folder. It is only a name until you read or write it. For small files, read_text() and write_text() do the whole job in one line.

python
from pathlib import Path

p = Path("a.txt")
print(p.write_text("a\nb\n", encoding="utf-8"))  # 4
print(repr(p.read_text(encoding="utf-8")))      # 'a\nb\n'

text = p.read_text(encoding="utf-8")
print(text.splitlines())                        # ['a', 'b']

write_text() creates the file, or replaces it if it exists, and returns how many characters it wrote. splitlines() gives a list of lines without the \n at the end of each one.

Binary data (images, zip files) is bytes, not text, so it takes no encoding:

python
print(Path("b.bin").write_bytes(b"\x00\xff"))  # 2
print(Path("b.bin").read_bytes())              # b'\x00\xff'
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

2. open() and with

open() gives you a file object. The with block closes the file when the block ends, even after an error.

python
with open("a.txt", encoding="utf-8") as f:
    text = f.read()            # the whole file as one string
print(repr(text))

with open("a.txt", encoding="utf-8") as f:
    for line in f:             # one line at a time, so huge files are fine
        print(line.rstrip("\n"))
python
'a\nb\n'
a
b
+ setup added so this can run · defines a, b
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

a = _AutoMock('a')
b = _AutoMock('b')

Other ways to read from an open file:

CallWhat it gives you
f.read()The rest of the file as one string
f.read(1024)The next 1024 characters (bytes in binary mode)
f.readline()The next line, with its \n
f.readlines()A list of all remaining lines, each with its \n

To add to a file, open it with mode "a". write() adds no newline for you, but print(..., file=f) does.

python
with open("log.txt", "a", encoding="utf-8") as f:
    f.write("start\n")
    f.writelines(["Ada logged in\n", "Grace logged in\n"])  # no newlines added
    print("ready", file=f)

print(Path("log.txt").read_text(encoding="utf-8").splitlines())
# ['start', 'Ada logged in', 'Grace logged in', 'ready']
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

The full list of options for open() (a summary, not code to run):

text
open(file,
     mode="r",
     buffering=-1,
     encoding=None,      # pass "utf-8"; the default depends on the computer
     errors="strict",    # "replace" puts a marker in place of bad bytes instead of failing
     newline=None,       # use "" for csv files
     closefd=True)

3. Modes

The mode is the second argument to open(). It says whether you read or write, and what happens to a file that already exists.

ModeReadWriteEmpties the file firstCreates the fileStarts at
"r" (default)yesnonono, it must existstart
"w"noyesyesyesstart
"x"noyesnoyes, FileExistsError if it existsstart
"a"noyesnoyesend
"r+"yesyesnonostart
"w+"yesyesyesyesstart
"a+"yesyesnoyesend

Add b for binary mode ("rb", "wb", "ab", "r+b"). In binary mode you read and write bytes, and you do not pass encoding=.

4. Useful helpers

python · run on your machine
from pathlib import Path

# A file next to the script, wherever you run the script from
data_file = Path(__file__).parent / "data.txt"
python
import os
from tempfile import TemporaryDirectory

# A scratch folder that is deleted when the block ends
with TemporaryDirectory() as tmp:
    print(Path(tmp).exists())  # True

# Older code joins paths with os.path.join; Path / does the same job
print(os.path.join("data", "a.txt") == str(Path("data") / "a.txt"))  # True
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

5. Path parts

A Path splits a file name into useful parts. The / operator joins parts with the right separator for the operating system (\ on Windows, / elsewhere). as_posix() shows a path with / so the output is the same everywhere.

python
p = Path("data/sales/report.csv")
print(p.name)                         # report.csv
print(p.stem)                         # report
print(p.suffix)                       # .csv
print(p.parent.as_posix())            # data/sales
print(p.parts)                        # ('data', 'sales', 'report.csv')
print(p.with_suffix(".json").name)    # report.json
print((Path("data") / "a.txt").as_posix())  # data/a.txt
print(Path("a.tar.gz").suffixes)      # ['.tar', '.gz']
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

stem is the name without its suffix, and parent is the folder the file is in. Path.cwd() is the folder you are working in, Path.home() is your home folder, and p.resolve() gives the full absolute path.

6. Check, list and tidy

python
import shutil

d = Path("data")
(d / "sub").mkdir(parents=True, exist_ok=True)   # parents=True also makes missing parent folders
for n in ["a.csv", "b.csv", "sub/x.md"]:
    (d / n).write_text("x", encoding="utf-8")

print(d.is_dir())                         # True
print((d / "a.csv").is_file())            # True
print((d / "zz.csv").exists())            # False
print((d / "a.csv").stat().st_size)       # 1
print(sorted(q.name for q in d.glob("*.csv")))   # ['a.csv', 'b.csv']
print(sorted(q.name for q in d.iterdir()))       # ['a.csv', 'b.csv', 'sub']
print([q.as_posix() for q in d.rglob("*.md")])   # ['data/sub/x.md']
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

stat().st_size is the size in bytes. glob() finds names that match a pattern in one folder. rglob() also searches every subfolder. The order of the results is not fixed, so sort them when the order matters. iterdir() lists the files and folders inside.

To rename, copy and delete:

python
Path("out").mkdir(exist_ok=True)            # exist_ok=True: no error if the folder already exists
(d / "b.csv").rename(d / "c.csv")           # rename or move
shutil.copy(d / "a.csv", "out")             # copy a file into the folder "out"
(d / "c.csv").unlink(missing_ok=True)       # delete a file
shutil.rmtree("out")                        # delete a folder and everything in it
print(sorted(q.name for q in d.iterdir()))  # ['a.csv', 'sub']
+ setup added so this can run · re-runs 2 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
shutil = _AutoMock('shutil')
def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
    import shutil
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
    'import shutil\n\nd = Path("data")\n(d / "sub").mkdir(parents=True, exist_ok=True)   # parents=True also makes missing parent folders\nfor n in ["a.csv", "b.csv", "sub/x.md"]:\n    (d / n).write_text("x", encoding="utf-8")\n\nprint(d.is_dir())                         # True\nprint((d / "a.csv").is_file())            # True\nprint((d / "zz.csv").exists())            # False\nprint((d / "a.csv").stat().st_size)       # 1\nprint(sorted(q.name for q in d.glob("*.csv")))   # [\'a.csv\', \'b.csv\']\nprint(sorted(q.name for q in d.iterdir()))       # [\'a.csv\', \'b.csv\', \'sub\']\nprint([q.as_posix() for q in d.rglob("*.md")])   # [\'data/sub/x.md\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

7. CSV

A CSV file is a table saved as text, one row per line. Open CSV files with newline="": the csv module handles line endings itself, and without newline="" you get extra blank rows on Windows.

python
import csv

rows = [["name", "age"], ["Ada", 36]]
with open("people.csv", "w", newline="", encoding="utf-8") as f:
    csv.writer(f).writerows(rows)

with open("people.csv", newline="", encoding="utf-8") as f:
    back = list(csv.DictReader(f))
print(back)  # [{'name': 'Ada', 'age': '36'}]

DictReader uses the header row as the keys. Every value comes back as a string, so 36 is now '36'. csv.reader(f) gives plain lists instead, with the header row included.

8. JSON

JSON is a text format for nested data: dicts, lists, strings, numbers, True/False and None. dumps() and loads() work with strings. dump() and load() work with open files.

python
import json

cfg = {"name": "Ada", "tags": ["x"]}
p = Path("cfg.json")
p.write_text(json.dumps(cfg, indent=2), encoding="utf-8")   # indent=2 puts each item on its own line
print(json.loads(p.read_text(encoding="utf-8")))           # {'name': 'Ada', 'tags': ['x']}

with open(p, encoding="utf-8") as f:
    cfg = json.load(f)
print(cfg["name"])                                     # Ada

print(json.dumps({"a": None, "b": True}))              # {"a": null, "b": true}
print(json.dumps("é"))                                 # "\u00e9"
print(json.dumps("é", ensure_ascii=False))             # "é"
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

ensure_ascii=False keeps accented letters as they are, so the file stays readable.

9. Big binary files

Read a big file in pieces so it never has to fit in memory at once. f.tell() gives the current position in bytes and f.seek() moves to a position.

python
with open("b.bin", "rb") as f:
    while chunk := f.read(65536):     # 64 KB at a time
        print(len(chunk))             # 2
    print(f.tell())                   # 2
    f.seek(0)                         # back to the start
    print(f.read(1))                  # b'\x00'
+ setup added so this can run · defines chunk
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

chunk = _AutoMock('chunk')

f.seek(-1, 2) means "1 byte before the end" (the second argument is 0 for the start, 1 for the current position, 2 for the end). In text mode, only f.seek(0) and positions you got from f.tell() are safe.

10. Errors you will meet

python
for action in [lambda: open("nope.txt"),
               lambda: open("a.txt", "x"),
               lambda: Path("data").mkdir(),
               lambda: Path("data").rmdir()]:
    try:
        action()
    except OSError as e:
        print(type(e).__name__)
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier
python
FileNotFoundError
FileExistsError
FileExistsError
OSError
ErrorUsual causeFix
FileNotFoundErrorWrong name, or Python is running in another folderCheck the name and Path.cwd(). For writing, create the folder with mkdir(parents=True)
FileExistsErrorMode "x", or mkdir() on a folder that existsUse "w", or pass exist_ok=True
OSError from rmdir()The folder is not emptyshutil.rmtree() deletes a folder and its contents
PermissionErrorThe file is open in another program, or you have no right to write thereClose the other program, or write to your own folder
UnicodeDecodeErrorThe file was read without the right encodingPass encoding="utf-8" (or the file's real encoding)

Common mistakes

No encoding. open("data.txt") uses the computer's default encoding, which on Windows is often not UTF-8. Accented letters then come out wrong or raise UnicodeDecodeError. Fix: open("data.txt", encoding="utf-8").

Mode "w" when you meant to add. open("notes.txt", "w") empties the file the moment it opens. Fix: use "a" to add to the end.

Backslashes in Windows paths. In "C:\new\table.csv" the \n becomes a newline and \t becomes a tab. Fix: write r"C:\new\table.csv" or use forward slashes, "C:/new/table.csv".

Reading after the with block. The file is closed when the block ends, so f.read() after it raises ValueError: I/O operation on closed file. Fix: read inside the block and keep the text in a variable.

Reading twice. The second f.read() returns "" because you are already at the end of the file:

python
with open("a.txt", encoding="utf-8") as f:
    print(repr(f.read()))  # 'a\nb\n'
    print(repr(f.read()))  # ''
    f.seek(0)              # fix: go back to the start
    print(repr(f.read()))  # 'a\nb\n'

Relative paths. open("data.txt") looks in the folder where you ran Python, not the folder that holds the script. Fix: Path(__file__).parent / "data.txt".

Comparing a line with its newline. A line read from a file ends in "\n", so line == "yes" is never true:

python
Path("answer.txt").write_text("yes\n", encoding="utf-8")
with open("answer.txt", encoding="utf-8") as f:
    line = f.readline()
print(line == "yes")          # False
print(line.strip() == "yes")  # True
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def Path(*_a, **_kw):
    print('-> Path() called')
    return _AutoMock('Path()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from pathlib import Path
except Exception:
    pass
_pm_earlier = [
    'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8"))  # 4\nprint(repr(p.read_text(encoding="utf-8")))      # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines())                        # [\'a\', \'b\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

See also

Practice this

on practicepython.in

Short exercises that run in your browser and tell you what your code did, as well as whether a test passed.

Found this useful? Share it with a friend who’s learning Python.