Using Your Files

A Python script can read and save files in its own folder: a CSV of data to chart, a settings file, a results file, a game's high score. Put import files at the top, and every file name you use means a file next to your .py file.

Read and write files in Python: import files

import files

with open("notes.txt", "w") as f:
    f.write("Hello from Python!")

print(open("notes.txt").read())

import files makes the folder your script is saved in its working folder. After it, Python's ordinary ways of reading and writing files all work with names relative to that folder:

  • open("data.csv") opens data.csv beside your script.
  • open("out/result.json", "w") writes into the out folder beside it. Folders inside your folder work the same way; make a new one with os.makedirs("out", exist_ok=True).
  • pathlib.Path("notes.txt").read_text(), csv, json and every other library that opens files by name use the same folder.

Save the script first: it uses the folder the .py file is saved in. It works the same whether you press Run with the preview off (plain Python) or the file draws in the preview.

Two more things come with it:

NameWhat it is
files.folderThe full path of your script's folder
files.path("data", "scores.csv")A full path to a file inside that folder

from files import path works too, if you only want path.

Read a CSV file and print a summary

Say sales.csv, next to your script, holds:

day,cups
Mon,12
Tue,19
Wed,7
Thu,15
Fri,22
import files
import csv

with open("sales.csv", newline="") as f:
    rows = list(csv.DictReader(f))

cups = [int(row["cups"]) for row in rows]
best = max(rows, key=lambda row: int(row["cups"]))

print(f"{len(rows)} days, {sum(cups)} cups in all")
print(f"Average: {sum(cups) / len(cups):.1f} a day")
print(f"Best day: {best['day']} with {best['cups']} cups")

csv.DictReader reads each line into a dictionary named by the first line, so row["cups"] is that day's number, as text; int(...) turns it into a number. The live chart tutorial shows how to draw the same numbers as a bar chart.

Save results to a file

import files
import json, os, random

rolls = [random.randint(1, 6) for _ in range(1000)]
counts = {face: rolls.count(face) for face in range(1, 7)}

os.makedirs("results", exist_ok=True)

# a readable text report
with open("results/dice.txt", "w") as f:
    f.write("1000 dice rolls\n")
    for face, n in counts.items():
        f.write(f"{face}: {n}\n")

# the same numbers as JSON, for another program to read
with open("results/dice.json", "w") as f:
    json.dump(counts, f, indent=2)

print("Saved to", files.path("results", "dice.txt"))

Opening with "w" makes the file, or replaces what was in it. Use "a" to add to the end instead, for a log that grows each run.

Read a settings file

Keep the numbers you like to change in a JSON file beside the script, and read them at the start. Here settings.json holds:

{"title": "My spiral", "dots": 300, "hue": 200, "spin": 0.4}
import files
import json, math

with open("settings.json") as f:
    settings = json.load(f)

t = 0

def update(dt):
    global t
    t += dt

def draw():
    ctx.fill_style = "#0c0c14"
    ctx.fill_rect(0, 0, width, height)
    for i in range(settings["dots"]):
        a = i * 0.3 + t * settings["spin"]
        r = i * 0.5
        ctx.fill_style = f"hsl({settings['hue'] + i % 60}, 80%, 60%)"
        ctx.fill_rect(width / 2 + math.cos(a) * r, height / 2 + math.sin(a) * r, 3, 3)
    ctx.fill_style = "white"
    ctx.font = "20px sans-serif"
    ctx.fill_text(settings["title"], 16, 30)

Change a number in settings.json, save it, and press Run: the drawing changes without touching the code. settings.get("dots", 300) gives a default for a setting the file leaves out.

Keep a high score

A game can remember the best score between runs: read it from a file when the game starts, and write it when a game ends with a new best.

# Arrow keys or drag to move. After game over, tap or press space to play again.
import files
import random

def load_best():
    try:
        with open("highscore.txt") as f:
            return int(f.read())
    except (FileNotFoundError, ValueError):
        return 0                     # no file yet: the first game

def save_best(score):
    with open("highscore.txt", "w") as f:
        f.write(str(score))

best = load_best()
player = Sprite("🧺", 200, 300, size=56)

def new_star():
    star = Sprite("⭐", random.uniform(20, width - 20), random.uniform(-300, -20), size=36)
    star.vy = random.uniform(120, 220)
    return star

def start():
    global score, lives, playing, stars
    score, lives, playing = 0, 3, True
    stars = [new_star() for _ in range(5)]

start()

def on_mouse_down(x, y):
    if not playing:
        start()

def on_key_down(key):
    if key == " " and not playing:
        start()

def update(dt):
    global score, lives, playing, best
    if not playing:
        return
    if "ArrowLeft" in keys:
        player.x -= 350 * dt
    if "ArrowRight" in keys:
        player.x += 350 * dt
    if mouse.down:
        player.x += (mouse.x - player.x) * 0.25
    player.x = min(max(player.x, 30), width - 30)
    player.y = height - 50
    for i, star in enumerate(stars):
        star.update(dt)
        if star.touches(player):
            score += 1
            stars[i] = new_star()
        elif star.y > height + 20:
            lives -= 1
            stars[i] = new_star()
    if lives <= 0:
        playing = False
        if score > best:             # a new best: remember it
            best = score
            save_best(best)

def draw():
    ctx.fill_style = "#0b1026"
    ctx.fill_rect(0, 0, width, height)
    for star in stars:
        star.draw()
    player.draw()
    ctx.fill_style = "white"
    ctx.font = "bold 20px sans-serif"
    ctx.text_align = "left"
    ctx.fill_text(f"Score: {score}   Best: {best}", 14, 30)
    ctx.text_align = "right"
    ctx.fill_text("❤️" * lives, width - 14, 30)
    if not playing:
        ctx.text_align = "center"
        ctx.font = "bold 40px sans-serif"
        ctx.fill_text("Game over", width / 2, height / 2)

  • load_best() reads the file. The first time there is no file yet, so it starts from 0.
  • save_best() writes the new best when a game ends, so the next run, or the next day, starts from it.
  • highscore.txt is an ordinary text file beside the game; open it to see the number, or delete it to start afresh.

The simple game tutorial builds this kind of game step by step.