Python Arena
Step 3 of 4

Writing CSV files

A CSV file is written like any other text file, one line at a time. Each line is the values of one record with the separator between them, and a line break at the end. An f-string builds such a line neatly. Here the data comes from a dictionary, with a header line first:

stock = {"pens": 120, "notebooks": 45, "staplers": 7}

with open("stock.csv", "w") as file:
    file.write("item;amount\n")
    for item, amount in stock.items():
        file.write(f"{item};{amount}\n")

with open("stock.csv") as file:
    print(file.read())
Output
item;amount
pens;120
notebooks;45
staplers;7

The f-string turns the numbers into text on the way, so write gets the string it needs.

Lines with many values

Some records hold a whole list of values, such as a day's temperature readings, and the lists can be of different lengths. An f-string needs a {...} for every value, so it can't write a list whose length it doesn't know. The string method join, from the module on strings and lists, can: it puts the separator between the strings of a list, and never after the last one. join only accepts strings, so the loop below turns each number into one with str first:

readings = {
    "Mon": [12, 15, 9, 11],
    "Tue": [14, 16],
}

with open("readings.csv", "w") as file:
    for day, values in readings.items():
        fields = [day]
        for value in values:
            fields.append(str(value))
        file.write(";".join(fields) + "\n")

with open("readings.csv") as file:
    print(file.read())
Output
Mon;12;15;9;11
Tue;14;16

The list fields starts with the day, and every reading is added after it as a string. Leave out the str, and join stops at the first number with TypeError: sequence item 1: expected str instance, int found.

Read, process, write

Many programs read data from one file, work something out and write the results to another file. Such a program is easiest to write, and to fix, in pieces: a function for each part. Here a shop keeps its prices in prices.csv, and for a sale it wants a second file with the old and the new price of every item:

The file prices.csv
item;price
coffee;3.20
bagel;2.50
juice;4.00
def read_prices(filename):
    prices = {}
    with open(filename) as file:
        for line in file:
            parts = line.strip().split(";")
            if parts[0] == "item":
                continue
            prices[parts[0]] = float(parts[1])
    return prices


def sale_lines(prices, percent):
    lines = ["item;price;sale price"]
    for item, price in prices.items():
        sale = price * (100 - percent) / 100
        lines.append(f"{item};{price:.2f};{sale:.2f}")
    return lines


def write_lines(filename, lines):
    with open(filename, "w") as file:
        for line in lines:
            file.write(line + "\n")


prices = read_prices("prices.csv")
write_lines("sale.csv", sale_lines(prices, 20))

with open("sale.csv") as file:
    print(file.read())
Output
item;price;sale price
coffee;3.20;2.56
bagel;2.50;2.00
juice;4.00;3.20

Look at what each function needs to know. read_prices is the only one that knows what the input file looks like, header line and all. sale_lines never touches a file: it gets a dictionary and returns a list of strings, so you can try it out with print(sale_lines({"tea": 2.0}, 50)) before any file exists. And write_lines knows nothing about prices: it writes any list of lines into any file, so your next program can use it just as it is. For a sale of 30 percent, only the number in the call changes.

Your task

Challenge 1 of 3

Fill in the blanks so the program writes the scores in the dictionary to scores.csv, one name;score line per player. The file should hold:

Ada;31
Ben;27
Cleo;35

Challenge 2

Write a function save_inventory(filename, inventory). The parameter inventory is a dictionary from item names to amounts. The function writes the file with a header line item;count and then one item;count line per item, in the dictionary's order.

For example, save_inventory("inventory.csv", {"bolts": 250, "nuts": 310, "washers": 75}) writes this file:

item;count
bolts;250
nuts;310
washers;75

Challenge 3

Each line of an exam file has a student's name and points (name;points). Write a function write_results(source, target) that reads the file source and writes the file target, where each line has the name, the points and pass (50 points or more) or fail.

If exam.csv has

Ana;72
Bo;49
Cy;50

then write_results("exam.csv", "results.csv") writes this results.csv:

Ana;72;pass
Bo;49;fail
Cy;50;pass