Modules, files, and JSON
Import reusable code and exchange structured data
Imports
A module is a Python file containing reusable definitions. import json binds the module name; from pathlib import Path imports a named object.
A module's top-level statements normally execute once per interpreter session. Keep prompts and file writes out of import-time code. Put entry-point behavior under if __name__ == "__main__":. A local file named json.py can shadow the standard library module.
Files and JSON
Specify encoding="utf-8" for text files. A with block closes the handle when its scope exits, including after a parsing failure.
JSON objects map to dictionaries, arrays to lists, and null to None. Object keys are strings; integer dictionary keys change type on round trip, and tuples become arrays. Valid JSON syntax does not establish the expected application schema.
Python's encoder accepts non-finite floats by default, though those tokens are outside standard JSON:
import json
payload = {"latency_ms": float("nan")}
print(json.dumps(payload)) # {"latency_ms": NaN}
try:
json.dumps(payload, allow_nan=False)
except ValueError:
print("non-finite measurement rejected")
print(json.loads('{"enabled": false}')["enabled"]) # Falseallow_nan=False rejects them while encoding. Strict decoding needs its own policy, including parse_constant; field types and ranges still need validation.
Updating an existing file
Opening in "w" mode truncates the destination immediately. Serialize first so a serialization error cannot destroy the previous content:
import json
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as directory:
destination = Path(directory) / "settings.json"
destination.write_text('{"version": 1}', encoding="utf-8")
encoded = json.dumps({"version": 2}, allow_nan=False)
staged = destination.with_suffix(".tmp")
staged.write_text(encoded, encoding="utf-8")
staged.replace(destination)
print(json.loads(destination.read_text(encoding="utf-8"))["version"]) # 2This single-writer example stages the file in the same directory, then replaces the destination. Atomic rename, where supported, prevents readers seeing a partial destination. It does not guarantee crash durability or a transaction across multiple files. Concurrent writers also need unique staging names and a conflict policy.
Gotchas
- Missing files, invalid permissions, malformed text, and invalid application data are different failures. Defaulting after every error can hide a deployment defect.
- Bound input size; syntactically valid JSON can still consume excessive memory.
- Do not use
evalto parse data. Use a database when updates require queries, concurrency control, or transactions.
Put it into practice
Implement the exercise in a full-screen workspace, then run its tests.