File Handling in Python

By the Syncfusion DevTutorials team · Last updated

Opening, the nine modes, three ways to read, why closing matters, the with statement, file errors, and text, CSV and JSON files.

Everything you have written so far has disappeared.

Every mark, every name, every list — gone the moment the program ended. A variable lives in memory, and memory is cleared when the program stops.

A school cannot work that way. The marks have to still be there tomorrow morning. That means writing them somewhere that outlives the program: a file.

Here is that idea in action — a file that keeps its contents after the program has ended:

Something that survives the program

with open("fh_first.txt", "w") as sheet:
    sheet.write("Asha,78\n")
    sheet.write("Ravi,85\n")

with open("fh_first.txt") as sheet:
    print(sheet.read())

A file is storage that outlives your program. Python opens one, hands you an object to read from or write to, and the file must be closed when you are done — which is why with exists.

What You Will Learn

In this lesson, you will learn:

  • What a file object is, and why opening one is not the same as reading it
  • All nine opening modes, and the one difference that matters most: w destroys, a adds
  • Three ways to read, and when each is the right one
  • Why an unclosed file can lose data you thought you had written
  • The with statement, and why nothing else in this lesson matters as much
  • How to handle a file that is missing, locked or not what you expected
  • Text, CSV and JSON — the three file shapes you will actually meet

By the end of this lesson, you will be able to open files in each mode, read them back three ways, write text safely, explain why closing matters, use with so closing happens on every path, handle missing files, and choose between plain text, CSV and JSON.

Every cell on this page writes its own file before reading it, so you can run any cell on its own and get the same answer. The files live in the browser tab, not on your computer, and they disappear when you reload the page.

Introduction to File Handling

What File Handling Is

File handling is the set of things a program does with files: open, read, write, close. Python builds all of it on one function, open(), and one kind of object, the file object.

File Operations in Python

There are four, and they always happen in this order:

  1. Open the file, saying what you intend to do with it.
  2. Read from it, or write to it.
  3. Close it.

Step 3 is the one people forget, and it is the one that loses data.

Working with Files

Here are the four steps, written out longhand:

The four steps, written out the long way

sheet = open("fh_steps.txt", "w")
sheet.write("Asha,78\n")
sheet.close()

sheet = open("fh_steps.txt", "r")
print(sheet.read())
sheet.close()

That works, and you will see it in older code. Later on this page you will replace all six lines with four better ones.

File Objects

open() does not give you the contents of the file. It gives you a file object: a thing that knows which file it is attached to, what you are allowed to do with it, and where you have got to in it.

A file object is not the text

sheet = open("fh_object.txt", "w")
print(sheet)
print(type(sheet).__name__)
sheet.close()

That position marker is the part beginners miss. Reading a file moves it to the end, and a second read from the same object gives you nothing.

The second read comes back empty

with open("fh_twice.txt", "w") as sheet:
    sheet.write("Asha,78\n")

with open("fh_twice.txt") as sheet:
    print("First read:", repr(sheet.read()))
    print("Second read:", repr(sheet.read()))

Nothing is wrong with the file. The marker is simply sitting at the end.

Opening Files

Using the open() Function

The function takes a file name and, optionally, a mode:

file_object = open(filename, mode)

filename is the name of the file. mode says what you intend to do. If you leave the mode out, you get "r" — read.

The mode is optional and defaults to read

with open("fh_default.txt", "w") as sheet:
    sheet.write("Asha,78\n")

with open("fh_default.txt") as sheet:
    print(sheet.read().strip())

File Opening Modes

There are nine modes. The table is the reference; the two that matter most are w and a:

ModeMeaningIf the file is missingIf it exists
rreadFileNotFoundErrorread from the start
wwritecreatedemptied first
aappendcreatedadded to at the end
xexclusive createcreatedFileExistsError
r+read and writeFileNotFoundErrorkept, marker at the start
w+write and readcreatedemptied first
a+append and readcreatedkept, writes go to the end

Add b for binary or t for text to any of them. t is the default, so "r" and "rt" are the same thing.

w empties the file the instant it is opened — before you write a single character. If you meant to add to a file and typed w, the old contents are gone, and there is no undo. When in doubt, use a.

File Properties

A file object will tell you about itself.

name, mode and closed

sheet = open("fh_props.txt", "w")
print("name:", sheet.name)
print("mode:", sheet.mode)
print("closed:", sheet.closed)

sheet.write("Asha,78\n")
sheet.close()

print("closed after close():", sheet.closed)

closed is the useful one: it is how you check whether a file still needs closing.

File Modes

Now each mode in turn, with the ones you will actually use first.

Read Mode (r)

Opens an existing file for reading, with the marker at the start. The file must already exist.

Reading a file that is there

with open("fh_read.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\n")

with open("fh_read.txt", "r") as sheet:
    print(sheet.read())

If the file is not there, the result is an exception:

Reading a file that is not there

with open("fh_nothing_here.txt", "r") as sheet:
    print(sheet.read())

Write Mode (w)

Creates the file if it is missing, and empties it if it is not.

w empties the file first

with open("fh_write.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\nMeera,60\n")

with open("fh_write.txt") as sheet:
    print("Before:", repr(sheet.read()))

with open("fh_write.txt", "w") as sheet:
    sheet.write("Dev,70\n")

with open("fh_write.txt") as sheet:
    print("After: ", repr(sheet.read()))

Three marks went in and one came out. That is w doing exactly what it says.

Append Mode (a)

Creates the file if it is missing, and adds to the end if it is not. Nothing already there is touched.

a adds to the end

with open("fh_append.txt", "w") as sheet:
    sheet.write("Asha,78\n")

with open("fh_append.txt", "a") as sheet:
    sheet.write("Ravi,85\n")

with open("fh_append.txt", "a") as sheet:
    sheet.write("Meera,60\n")

with open("fh_append.txt") as sheet:
    print(sheet.read())

This is the mode for a log, an attendance register, or anything that only ever grows.

Exclusive Create Mode (x)

Creates the file, and refuses if it already exists. Use it when overwriting would be a disaster.

x refuses to overwrite

import os

if os.path.exists("fh_exclusive.txt"):
    os.remove("fh_exclusive.txt")

with open("fh_exclusive.txt", "x") as sheet:
    sheet.write("Asha,78\n")
print("Created")

try:
    with open("fh_exclusive.txt", "x") as sheet:
        sheet.write("Ravi,85\n")
except FileExistsError as error:
    print("FileExistsError — refused to overwrite", error.filename)

The first three lines delete the file if it is there, so that you can press Run again and see the same thing.

Binary Mode (b)

Text mode gives you str and handles the encoding for you. Binary mode gives you bytes — the raw contents, untouched. Images, sound and compressed files are all binary.

bytes, not text

with open("fh_binary.bin", "wb") as data:
    data.write(b"Asha 78")

with open("fh_binary.bin", "rb") as data:
    raw = data.read()

print(type(raw).__name__, raw)
print(raw.decode("utf-8"))

Note the b before the quotes: b"Asha 78" is a bytes literal, not text.

Text Mode (t)

The default. Python decodes the bytes into text for you, and converts line endings so that \n works the same on every operating system. You almost never type t, because leaving it out means the same thing.

Read and Write Mode (r+)

Opens an existing file for both, with the marker at the start. Nothing is emptied.

r+ keeps what is there

with open("fh_rplus.txt", "w") as sheet:
    sheet.write("Asha,78\n")

with open("fh_rplus.txt", "r+") as sheet:
    print("Read first:", repr(sheet.read()))
    sheet.write("Ravi,85\n")

with open("fh_rplus.txt") as sheet:
    print("Now holds: ", repr(sheet.read()))

The write landed at the end because reading had already moved the marker there. Had you written first, you would have overwritten the start.

Write and Read Mode (w+)

Both, but it empties the file on open. So the first read gives you nothing — there is nothing left to read.

w+ empties first, then lets you read

with open("fh_wplus.txt", "w") as sheet:
    sheet.write("old marks\n")

with open("fh_wplus.txt", "w+") as sheet:
    print("Read straight after opening:", repr(sheet.read()))
    sheet.write("Asha,78\n")
    sheet.seek(0)
    print("After seek(0):", repr(sheet.read()))

seek(0) moves the marker back to the start. Without it, the read after a write returns nothing.

Append and Read Mode (a+)

Both, nothing is emptied, and every write goes to the end no matter where the marker is.

a+ always writes at the end

with open("fh_aplus.txt", "w") as sheet:
    sheet.write("Asha,78\n")

with open("fh_aplus.txt", "a+") as sheet:
    sheet.write("Ravi,85\n")
    sheet.seek(0)
    print(sheet.read())

Reading Files

The modes are done. Now the three ways to actually get text out of a file — and the fourth way, the one to prefer.

Reading the Whole File with read()

read() returns the entire file as one string.

read gives you one string

with open("fh_whole.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\nMeera,60\n")

with open("fh_whole.txt") as sheet:
    contents = sheet.read()

print(repr(contents))
print(len(contents), "characters")

Good for a small file. A large one goes entirely into memory, so this is not the tool for a file bigger than your computer is comfortable with.

Reading One Line with readline()

readline() returns the next line, including its \n, and moves the marker past it.

readline, one call at a time

with open("fh_lines.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\nMeera,60\n")

with open("fh_lines.txt") as sheet:
    print(repr(sheet.readline()))
    print(repr(sheet.readline()))
    print(repr(sheet.readline()))
    print(repr(sheet.readline()))

The last call returns an empty string. That is how readline() says there is nothing left — not None, and not an exception.

Reading All Lines with readlines()

readlines() returns a list, one item per line, each still carrying its \n.

readlines gives you a list

with open("fh_all.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\nMeera,60\n")

with open("fh_all.txt") as sheet:
    rows = sheet.readlines()

print(rows)
print(len(rows), "rows")

for row in rows:
    name, mark = row.strip().split(",")
    print(name, "scored", mark)

Looping Over the File Itself

This is the one you should reach for. A file object can be looped over directly, and it reads one line at a time instead of loading everything.

The usual way to read a file

with open("fh_loop.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\nMeera,60\n")

total = 0
with open("fh_loop.txt") as sheet:
    for row in sheet:
        name, mark = row.strip().split(",")
        total += int(mark)
        print(f"{name:<8}{mark:>4}")

print("Total:", total)

Every line you read keeps its \n. .strip() removes it, along with any stray spaces. Forgetting it is why a printed name sometimes has a blank line under it, and why int(mark) sometimes works and sometimes does not.

Writing Files

Reading has three routes; writing is simpler — but the newline catches everyone.

Writing with write()

write() writes exactly the text you give it — and no newline. If you want lines, you add \n yourself.

write adds nothing of its own

with open("fh_nonewline.txt", "w") as sheet:
    sheet.write("Asha,78")
    sheet.write("Ravi,85")

with open("fh_nonewline.txt") as sheet:
    print(repr(sheet.read()))

Two writes, one line. That is almost never what you wanted.

With the newlines put back

with open("fh_newline.txt", "w") as sheet:
    sheet.write("Asha,78\n")
    sheet.write("Ravi,85\n")

with open("fh_newline.txt") as sheet:
    print(sheet.read())

write() also returns the number of characters it wrote, which is occasionally useful and often surprising.

write returns a count

with open("fh_count.txt", "w") as sheet:
    written = sheet.write("Asha,78\n")

print("characters written:", written)

Writing Several Lines with writelines()

writelines() takes a list of strings and writes them one after another. Despite the name, it does not add newlines either.

writelines, with and without newlines

rows = ["Asha,78", "Ravi,85", "Meera,60"]

with open("fh_wl_bad.txt", "w") as sheet:
    sheet.writelines(rows)

with open("fh_wl_bad.txt") as sheet:
    print("Without newlines:", repr(sheet.read()))

with open("fh_wl_good.txt", "w") as sheet:
    sheet.writelines(row + "\n" for row in rows)

with open("fh_wl_good.txt") as sheet:
    print("With newlines:")
    print(sheet.read())

Overwriting against Appending

The whole difference, side by side.

w against a, from the same starting point

for mode in ["w", "a"]:
    with open("fh_compare.txt", "w") as sheet:
        sheet.write("Asha,78\n")

    with open("fh_compare.txt", mode) as sheet:
        sheet.write("Ravi,85\n")

    with open("fh_compare.txt") as sheet:
        print(f"mode {mode!r} leaves:", repr(sheet.read()))

Closing Files

You have been using with since the first example. Here is what it saves you from.

Using close()

close, and what happens after it

sheet = open("fh_close.txt", "w")
sheet.write("Asha,78\n")
print("closed?", sheet.closed)

sheet.close()
print("closed?", sheet.closed)

try:
    sheet.write("Ravi,85\n")
except ValueError as error:
    print("ValueError:", error)

Why Closing Files Matters

Three reasons, and the first one is the one that bites.

  1. Your writing may not have happened yet. Python holds recent writes in a buffer and sends them to the file in batches. close() flushes that buffer. A program that ends badly without closing can leave the file short.
  2. An open file holds a resource. An operating system allows a limited number of open files at once, and a loop that opens thousands without closing them will eventually fail.
  3. Other programs may be locked out. On some systems a file that one program holds open cannot be opened by another.

You can watch reason 1 happen with your own eyes:

The contents only exist once it is closed

sheet = open("fh_flush.txt", "w")
sheet.write("Asha,78\n")

with open("fh_flush.txt") as check:
    print("While still open:", repr(check.read()))

sheet.close()

with open("fh_flush.txt") as check:
    print("After close():   ", repr(check.read()))

Run that and look at the first line. The text you "wrote" was not in the file yet.

close() written at the end of a block is not enough. If any line above it raises, the close() is skipped and the file stays open with its buffer unflushed. This is the problem with exists to solve, and it is why you should not write open() and close() by hand.

Context Managers

The with Statement

A context manager is an object that knows how to tidy up after itself. A file is one. with asks it to do that tidying automatically, at the end of the block — whichever way the block ends.

with open(filename, mode) as file_object:
    work with file_object

with closes the file for you

with open("fh_with.txt", "w") as sheet:
    sheet.write("Asha,78\n")
    print("inside the block, closed?", sheet.closed)

print("after the block, closed?", sheet.closed)

Nobody called close(). It happened anyway.

Automatic Closing, Even When Things Go Wrong

This is the part that matters. The file is closed even if the block raises.

Closed even on the way out of an exception

try:
    with open("fh_safe.txt", "w") as sheet:
        sheet.write("Asha,78\n")
        raise ValueError("something went wrong halfway through")
except ValueError as error:
    print("Caught:", error)

print("closed?", sheet.closed)

with open("fh_safe.txt") as sheet:
    print("and the write survived:", repr(sheet.read()))

The exception escaped, the file was closed on the way out, and the buffer was flushed. Hand-written close() would have been skipped.

Benefits of Context Managers

  • You cannot forget to close the file, because you never wrote the close.
  • The file is closed on every path out — normal end, return, or exception.
  • The block makes it obvious how long the file is open for.
  • Several files can be opened in one with, separated by commas.

Two files in one with

with open("fh_src.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\n")

with open("fh_src.txt") as source, open("fh_dest.txt", "w") as target:
    for row in source:
        target.write(row.upper())

with open("fh_dest.txt") as target:
    print(target.read())

Exception Handling in File Operations

Handling File Errors

Four things go wrong with files, and each has its own exception.

Four file failures, each named

jobs = ["missing", "readonly", "wrongmode", "closed"]

with open("fh_errors.txt", "w") as sheet:
    sheet.write("Asha,78\n")

for job in jobs:
    try:
        if job == "missing":
            open("fh_not_a_real_file.txt")
        elif job == "readonly":
            with open("fh_errors.txt", "w") as sheet:
                sheet.read()
        elif job == "wrongmode":
            with open("fh_errors.txt", "r") as sheet:
                sheet.write("Ravi,85\n")
        elif job == "closed":
            sheet = open("fh_errors.txt")
            sheet.close()
            sheet.read()
    except Exception as error:
        print(type(error).__name__ + ":", error)

UnsupportedOperation is a kind of ValueError, so except ValueError catches those middle two.

try-except-finally

The long form, written out so you can see what with is doing for you.

The same job without with

sheet = None
try:
    sheet = open("fh_not_a_real_file.txt")
    print(sheet.read())
except FileNotFoundError as error:
    print("Could not find", error.filename)
finally:
    if sheet is not None:
        sheet.close()
    print("finished either way")

Note sheet = None at the top and the if inside finally. Without them, a failed open() leaves sheet unset and the finally block raises a second exception on top of the first.

Safe File Operations

with and try together: with guarantees the close, try decides what happens when the file is not there.

The shape to use in real code

def read_marks(filename):
    try:
        with open(filename) as sheet:
            return [row.strip() for row in sheet if row.strip()]
    except FileNotFoundError:
        print(f"  ({filename} is missing — treating it as empty)")
        return []

with open("fh_marks.txt", "w") as sheet:
    sheet.write("Asha,78\nRavi,85\n")

print(read_marks("fh_marks.txt"))
print(read_marks("fh_no_such_sheet.txt"))

Do not check os.path.exists() and then open the file. Between the two lines the file can be deleted or renamed, and your careful check reports a file that is gone by the time you open it. Open it inside a try and handle FileNotFoundError — that covers both cases with one piece of code.

Working with File Types

Everything so far has been plain text. Real programs meet three shapes of file, and each has its own way back into values.

Text Files (.txt)

Plain text is the simplest case, and everything above has been working with it. The only decision is how you separate the parts of a row.

A text file read back into values

with open("fh_plain.txt", "w") as sheet:
    sheet.write("Asha,Maths,78\n")
    sheet.write("Ravi,Science,85\n")

with open("fh_plain.txt") as sheet:
    for row in sheet:
        name, subject, mark = row.strip().split(",")
        print(f"{name} scored {mark} in {subject}")

That works until a name contains a comma. Which is why CSV exists.

CSV Files (.csv)

A CSV file is a table: one row per line, values separated by commas. The csv module handles the awkward cases — commas inside a value, quotes, blank fields — that splitting by hand gets wrong.

Writing a CSV

import csv

with open("fh_marks.csv", "w", newline="") as sheet:
    writer = csv.writer(sheet)
    writer.writerow(["name", "subject", "mark"])
    writer.writerows([
        ["Asha", "Maths", 78],
        ["Ravi", "Science", 85],
        ["Meera", "English", 60],
    ])

with open("fh_marks.csv") as sheet:
    print(sheet.read())

newline="" is not optional. Without it, the csv module and the file object both add line endings, and the file comes out double-spaced on some systems.

Reading a CSV row by row

import csv

with open("fh_marks.csv", "w", newline="") as sheet:
    writer = csv.writer(sheet)
    writer.writerow(["name", "subject", "mark"])
    writer.writerows([
        ["Asha", "Maths", 78],
        ["Ravi", "Science", 85],
        ["Meera", "English", 60],
    ])

with open("fh_marks.csv", newline="") as sheet:
    for row in csv.reader(sheet):
        print(row)

Every value comes back as text, including the marks. 78 written is "78" read.

DictReader names the columns for you

import csv

with open("fh_marks.csv", "w", newline="") as sheet:
    writer = csv.writer(sheet)
    writer.writerow(["name", "subject", "mark"])
    writer.writerows([
        ["Asha", "Maths", 78],
        ["Ravi", "Science", 85],
        ["Meera", "English", 60],
    ])

total = 0
with open("fh_marks.csv", newline="") as sheet:
    for row in csv.DictReader(sheet):
        total += int(row["mark"])
        print(row["name"], "->", row["mark"])

print("Total:", total)

DictReader uses the first line as the column names, so you write row["mark"] instead of row[2] — and the code survives someone adding a column.

DictWriter writes dictionaries back out

import csv

records = [
    {"name": "Asha", "mark": 78},
    {"name": "Ravi", "mark": 85},
]

with open("fh_dict.csv", "w", newline="") as sheet:
    writer = csv.DictWriter(sheet, fieldnames=["name", "mark"])
    writer.writeheader()
    writer.writerows(records)

with open("fh_dict.csv") as sheet:
    print(sheet.read())

JSON Files (.json)

JSON is the format for nested data — a dictionary inside a dictionary inside a list. A CSV is flat; JSON is not.

Four function names, and the rule is simple: the ones ending in s work with strings, the ones without work with files.

FunctionDirectionWorks with
json.dump()Python → filea file
json.load()file → Pythona file
json.dumps()Python → texta string
json.loads()text → Pythona string

Writing JSON to a file

import json

record = {
    "name": "Asha",
    "class": "7A",
    "marks": {"Maths": 78, "Science": 85},
}

with open("fh_record.json", "w") as sheet:
    json.dump(record, sheet, indent=2)

with open("fh_record.json") as sheet:
    print(sheet.read())

indent=2 is what makes it readable. Leave it out and the whole thing is one long line.

Reading JSON back

import json

record = {
    "name": "Asha",
    "class": "7A",
    "marks": {"Maths": 78, "Science": 85},
}

with open("fh_record.json", "w") as sheet:
    json.dump(record, sheet, indent=2)

with open("fh_record.json") as sheet:
    loaded = json.load(sheet)

print(type(loaded).__name__)
print(loaded["name"], "is in", loaded["class"])
print("Maths:", loaded["marks"]["Maths"])

It comes back as a real dictionary, with the numbers still numbers. That is the advantage over CSV, where everything arrives as text.

The string versions

import json

record = {"name": "Asha", "mark": 78}

as_text = json.dumps(record)
print(as_text)
print(type(as_text).__name__)

back = json.loads(as_text)
print(back["mark"] + 1)

One last thing worth knowing — what happens when the JSON is not valid:

When the file is not valid JSON

import json

with open("fh_broken.json", "w") as sheet:
    sheet.write("{name: Asha}")

try:
    with open("fh_broken.json") as sheet:
        json.load(sheet)
except json.JSONDecodeError as error:
    print("JSONDecodeError on line", error.lineno)
    print("Problem:", error.msg)

print("It is a kind of ValueError:", issubclass(json.JSONDecodeError, ValueError))

JSON needs double quotes around every key and every piece of text. Single quotes are valid Python and invalid JSON, which is the single most common cause of that error.

Choose by shape, not by habit. A flat table of rows and columns is a CSV, and a spreadsheet will open it. Anything nested — a record containing a list, or a dictionary of dictionaries — is JSON. Plain text is for notes and logs, where nothing needs reading back as data.

Common mistakes

Before moving on, here are some of the mistakes beginners make with files.

Each row is one mistake. The middle column is what you will actually see on screen.

MistakeWhat you seeWhy it happensFix
open(name, "w") meaning to addThe old contents are gonew empties the file on openUse a
Reading a file twice from one objectThe second read is ''The marker is at the endseek(0), or open it again
No close() and no withThe file is short or emptyThe buffer was never flushedUse with
close() after a line that raisesThe file stays openThe close() line is skippedUse with
write() without \nEverything on one linewrite() adds nothing of its ownAdd "\n" yourself
writelines(rows) expecting linesEverything on one lineIt does not add newlines eitherwritelines(r + "\n" for r in rows)
Forgetting .strip() on a read lineA blank line between rows, or ValueError from int()Each line still carries its \nrow.strip()
csv.writer without newline=""A blank line between every rowTwo layers both add line endingsopen(name, "w", newline="")
Expecting numbers back from a CSVTypeError when you add themEvery CSV value reads as textint(row["mark"])
Single quotes in a JSON fileJSONDecodeErrorJSON only allows double quotesWrite it with json.dump()
os.path.exists() then open()A FileNotFoundError anywayThe file can vanish between the two linesOpen inside a try
Reading a huge file with read()The program slows or stopsThe whole file goes into memoryLoop over the file object

What to Remember

  • A file is storage that outlives the program. open() gives you a file object, not the contents.
  • A file object remembers where you have got to. A second read() gives you nothing until you seek(0).
  • w empties the file on open; a adds to the end. Getting this wrong destroys data with no undo.
  • x refuses to overwrite. b gives you bytes; t is text and is the default.
  • read() gives one string, readline() gives the next line, readlines() gives a list — and looping over the file object is usually better than all three.
  • Every line you read keeps its \n. Use .strip().
  • Neither write() nor writelines() adds a newline. You add it.
  • Closing flushes the buffer. Until a file is closed, what you wrote may not be in it.
  • Use with. It closes the file on every path out, including through an exception, and it is the reason you never need close().
  • Put the try around the open() and the with inside it. Catch FileNotFoundError by name.
  • CSV is a flat table and reads back as text. newline="" when writing, DictReader when reading.
  • JSON is for nested data and keeps types. dump/load for files, dumps/loads for strings.

Next: Advanced Functions